Skip to content

Commit

Permalink
Refactor ContestHeader into it's own component
Browse files Browse the repository at this point in the history
  • Loading branch information
wdsrocha committed Apr 12, 2021
1 parent f7d765e commit a2ee839
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 111 deletions.
89 changes: 89 additions & 0 deletions packages/app/components/ContestHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Card, Progress, Typography } from "antd";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import { useState } from "react";
import { useInterval } from "../hooks/useInterval";
import { useTwoPassRendering } from "../hooks/useTwoPassRendering";

dayjs.extend(duration);

const { Title, Text } = Typography;

const getTimeDifferenceFrom = (now: dayjs.Dayjs, target: dayjs.Dayjs) => {
const timeDifference = dayjs.duration(
target.isBefore(now) ? now.diff(target) : target.diff(now),
);
const days = timeDifference.days();

if (days > 1) return `${days} dias`;
if (days === 1) return `${Math.floor(timeDifference.asHours())} horas`;
return timeDifference.format("HH:mm:ss");
};

type ContestHeaderProps = Pick<Contest, "startDate" | "endDate" | "title">;

export const ContestHeader = ({
startDate: rawStartDate,
endDate: rawEndDate,
title,
}: ContestHeaderProps) => {
const isClient = useTwoPassRendering();

const startDate = dayjs(rawStartDate);
const endDate = dayjs(rawEndDate);
const formattedStartDate = startDate.format("YYYY-MM-DD HH:mm Z UTC");
const formattedEndDate = endDate.format("YYYY-MM-DD HH:mm Z UTC");

const [now, setNow] = useState(dayjs());

const contestStatus = (() => {
if (now.isBefore(startDate)) return "scheduled";
if (now.isBefore(endDate)) return "running";
return "ended";
})();

const relativeTimeInfo = (() => {
if (contestStatus === "scheduled") {
return `Iniciará em ${getTimeDifferenceFrom(now, startDate)}`;
}
if (contestStatus === "running") {
return `Tempo restante: ${getTimeDifferenceFrom(now, endDate)}`;
}
return <Text type="success">Acabou</Text>;
})();

const percentage = (() => {
if (contestStatus === "scheduled") return 0;
if (contestStatus === "ended") return 100;
return +(100 * (now.diff(startDate) / endDate.diff(startDate))).toFixed(2);
})();

useInterval(() => setNow(dayjs()), contestStatus !== "ended" ? 1000 : null);

return (
<Card
className="card"
bodyStyle={{ paddingTop: 16 }}
title={<Title level={1}>{title}</Title>}
>
<Progress
percent={isClient ? percentage : 0}
status={contestStatus === "running" ? "active" : undefined}
showInfo={false}
/>
<div className="flex flex-col md:flex-row md:justify-between md:items-center">
<div>
<Text strong>Início: </Text>
{formattedStartDate}
</div>
<div className="order-first md:order-none">
<Title level={5}>{isClient ? relativeTimeInfo : "..."}</Title>
</div>
<div>
<Text strong>Término: </Text>
{formattedEndDate}
</div>
</div>
</Card>
);
};
114 changes: 3 additions & 111 deletions packages/app/pages/contest/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,16 @@
import { CheckCircleFilled, CloseCircleFilled } from "@ant-design/icons";
import { Card, Progress, Tabs } from "antd";
import { Card, Tabs } from "antd";
import Table, { ColumnsType } from "antd/lib/table";
import Text from "antd/lib/typography/Text";
import Title from "antd/lib/typography/Title";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import { GetServerSideProps, InferGetServerSidePropsType } from "next";
import { HTMLAttributes, useState } from "react";
import { HTMLAttributes } from "react";
import classNames from "classnames";
import { useInterval } from "../../hooks/useInterval";
import { useTwoPassRendering } from "../../hooks/useTwoPassRendering";
import { Hyperlink } from "../../components/Hyperlink";

dayjs.extend(duration);
import { ContestHeader } from "../../components/ContestHeader";

type ProblemStatus = "not submitted" | "accepted" | "rejected";

const { TabPane } = Tabs;

interface ProblemOverview {
id: string;
origin: string;
title: string;
status: ProblemStatus;
solvedCount: number;
attemptedCount: number;
}

interface Contest {
title: string;
startDate: string;
endDate: string;
problems: ProblemOverview[];
}

interface ContestResponse {
data: Contest;
}
Expand Down Expand Up @@ -155,91 +132,6 @@ export const getServerSideProps: GetServerSideProps<ContestResponse> = async ()
},
});

type ContestHeaderProps = Pick<Contest, "startDate" | "endDate" | "title">;

const ContestHeader = ({
startDate: rawStartDate,
endDate: rawEndDate,
title,
}: ContestHeaderProps) => {
const isClient = useTwoPassRendering();

const startDate = dayjs(rawStartDate);
const endDate = dayjs(rawEndDate);
const formattedStartDate = startDate.format("YYYY-MM-DD HH:mm Z UTC");
const formattedEndDate = endDate.format("YYYY-MM-DD HH:mm Z UTC");

const [now, setNow] = useState(dayjs());

const getTimeDifferenceFrom = (date: dayjs.Dayjs) => {
const timeDifference = dayjs.duration(
date.isBefore(now) ? now.diff(date) : date.diff(now),
);
const days = timeDifference.days();

if (days > 1) {
return `${days} dias`;
}

if (days === 1) {
return `${Math.floor(timeDifference.asHours())} horas`;
}

return timeDifference.format("HH:mm:ss");
};

const contestStatus = (() => {
if (now.isBefore(startDate)) return "scheduled";
if (now.isBefore(endDate)) return "running";
return "ended";
})();

const relativeTimeInfo = (() => {
if (contestStatus === "scheduled") {
return `Iniciará em ${getTimeDifferenceFrom(startDate)}`;
}
if (contestStatus === "running") {
return `Tempo restante: ${getTimeDifferenceFrom(endDate)}`;
}
return <Text type="success">Acabou</Text>;
})();

const percentage = (() => {
if (contestStatus === "scheduled") return 0;
if (contestStatus === "ended") return 100;
return +(100 * (now.diff(startDate) / endDate.diff(startDate))).toFixed(2);
})();

useInterval(() => setNow(dayjs()), contestStatus !== "ended" ? 1000 : null);

return (
<Card
className="card"
bodyStyle={{ paddingTop: 16 }}
title={<Title level={1}>{title}</Title>}
>
<Progress
percent={isClient ? percentage : 0}
status={contestStatus === "running" ? "active" : undefined}
showInfo={false}
/>
<div className="flex flex-col md:flex-row md:justify-between md:items-center">
<div>
<Text strong>Início: </Text>
{formattedStartDate}
</div>
<div className="order-first md:order-none">
<Title level={5}>{isClient ? relativeTimeInfo : "..."}</Title>
</div>
<div>
<Text strong>Término: </Text>
{formattedEndDate}
</div>
</div>
</Card>
);
};

interface ContestProblemsProps {
problems: ProblemOverview[];
}
Expand Down
15 changes: 15 additions & 0 deletions packages/app/typings/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
declare interface ProblemOverview {
id: string;
origin: string;
title: string;
status: ProblemStatus;
solvedCount: number;
attemptedCount: number;
}

declare interface Contest {
title: string;
startDate: string;
endDate: string;
problems: ProblemOverview[];
}

0 comments on commit a2ee839

Please sign in to comment.