mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
Relative time voting timing info
This commit is contained in:
parent
79278c4cbc
commit
07cc6699f2
30
app/components/RelativeTime.tsx
Normal file
30
app/components/RelativeTime.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import type * as React from "react";
|
||||
import { useIsMounted } from "~/hooks/useIsMounted";
|
||||
|
||||
export function RelativeTime({
|
||||
children,
|
||||
timestamp,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
timestamp: number;
|
||||
}) {
|
||||
const isMounted = useIsMounted();
|
||||
|
||||
return (
|
||||
<abbr
|
||||
title={
|
||||
isMounted
|
||||
? new Date(timestamp).toLocaleString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
timeZoneName: "short",
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</abbr>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import shuffle from "just-shuffle";
|
||||
import invariant from "tiny-invariant";
|
||||
import type { MonthYear} from "~/modules/plus-server";
|
||||
import type { MonthYear } from "~/modules/plus-server";
|
||||
import { upcomingVoting } from "~/modules/plus-server";
|
||||
import { dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import type { Unpacked } from "~/utils/types";
|
||||
|
|
|
|||
11
app/hooks/useIsMounted.ts
Normal file
11
app/hooks/useIsMounted.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import * as React from "react";
|
||||
|
||||
export function useIsMounted() {
|
||||
const [isMounted, setIsMounted] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
return isMounted;
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ export function canSuggestNewUserBE({
|
|||
]);
|
||||
}
|
||||
|
||||
function isVotingActive() {
|
||||
export function isVotingActive() {
|
||||
const now = new Date();
|
||||
const { endDate, startDate } = monthsVotingRange({
|
||||
month: now.getMonth(),
|
||||
|
|
|
|||
|
|
@ -1,36 +1,76 @@
|
|||
import type { LoaderFunction } from "@remix-run/node";
|
||||
import { json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { formatDistance } from "date-fns";
|
||||
import { RelativeTime } from "~/components/RelativeTime";
|
||||
import { db } from "~/db";
|
||||
import type { UsersForVoting } from "~/db/models/plusVotes.server";
|
||||
import { getUser } from "~/modules/auth";
|
||||
import { monthsVotingRange, upcomingVoting } from "~/modules/plus-server";
|
||||
import { isVotingActive } from "~/permissions";
|
||||
|
||||
interface PlusVotingLoaderData {
|
||||
usersForVoting?: UsersForVoting;
|
||||
}
|
||||
type PlusVotingLoaderData =
|
||||
// voting is not active OR user is not eligible to vote
|
||||
| {
|
||||
type: "timeInfo";
|
||||
relativeTime: string;
|
||||
timestamp: number;
|
||||
timing: "starts" | "ends";
|
||||
}
|
||||
// user can vote
|
||||
| {
|
||||
type: "voting";
|
||||
usersForVoting?: UsersForVoting;
|
||||
}
|
||||
// user already voted
|
||||
| { type: "votingInfo"; votingInfo: { placeholder: true } };
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const user = await getUser(request);
|
||||
|
||||
const now = new Date();
|
||||
const { startDate, endDate } = monthsVotingRange(upcomingVoting(now));
|
||||
if (!isVotingActive()) {
|
||||
return json<PlusVotingLoaderData>({
|
||||
type: "timeInfo",
|
||||
relativeTime: formatDistance(startDate, now, { addSuffix: true }),
|
||||
timestamp: startDate.getTime(),
|
||||
timing: "starts",
|
||||
});
|
||||
}
|
||||
|
||||
const usersForVoting = db.plusVotes.usersForVoting(user);
|
||||
|
||||
if (!usersForVoting) {
|
||||
return json<PlusVotingLoaderData>({
|
||||
type: "timeInfo",
|
||||
relativeTime: formatDistance(endDate, now, { addSuffix: true }),
|
||||
timestamp: endDate.getTime(),
|
||||
timing: "ends",
|
||||
});
|
||||
}
|
||||
|
||||
return json<PlusVotingLoaderData>({
|
||||
usersForVoting: db.plusVotes.usersForVoting(user),
|
||||
type: "voting",
|
||||
usersForVoting,
|
||||
});
|
||||
};
|
||||
|
||||
export default function PlusVotingPage() {
|
||||
const data = useLoaderData();
|
||||
const data = useLoaderData<PlusVotingLoaderData>();
|
||||
|
||||
return <NextVotingInfo />;
|
||||
}
|
||||
if (data.type === "timeInfo") {
|
||||
return (
|
||||
<div className="text-sm text-center">
|
||||
{data.timing === "starts"
|
||||
? "Next voting starts"
|
||||
: "Voting is currently happening. Ends"}{" "}
|
||||
<RelativeTime timestamp={data.timestamp}>
|
||||
{data.relativeTime}
|
||||
</RelativeTime>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NextVotingInfo() {
|
||||
return (
|
||||
<div className="text-sm text-center">
|
||||
Next voting starts{" "}
|
||||
{monthsVotingRange(upcomingVoting(new Date())).startDate.toLocaleString(
|
||||
"en-US"
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,6 +334,14 @@ hr {
|
|||
border-color: var(--theme-transparent);
|
||||
}
|
||||
|
||||
abbr:not([title]) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
dialog {
|
||||
border: 0;
|
||||
margin: auto;
|
||||
|
|
|
|||
18
package-lock.json
generated
18
package-lock.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"better-sqlite3": "^7.5.3",
|
||||
"clsx": "^1.1.1",
|
||||
"countries-list": "^2.6.1",
|
||||
"date-fns": "^2.28.0",
|
||||
"fuse.js": "^6.6.2",
|
||||
"just-shuffle": "^4.0.1",
|
||||
"randomcolor": "^0.6.2",
|
||||
|
|
@ -4284,6 +4285,18 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "2.28.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
|
||||
"integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==",
|
||||
"engines": {
|
||||
"node": ">=0.11"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/date-fns"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.2",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz",
|
||||
|
|
@ -17365,6 +17378,11 @@
|
|||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og=="
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "2.28.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
|
||||
"integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw=="
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.11.2",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
"better-sqlite3": "^7.5.3",
|
||||
"clsx": "^1.1.1",
|
||||
"countries-list": "^2.6.1",
|
||||
"date-fns": "^2.28.0",
|
||||
"fuse.js": "^6.6.2",
|
||||
"just-shuffle": "^4.0.1",
|
||||
"randomcolor": "^0.6.2",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user