swap to ultra-fast APIs on homepage, finish continents
This commit is contained in:
parent
644e25f673
commit
9b439d0a19
14 changed files with 365 additions and 114 deletions
|
@ -11,6 +11,15 @@ export const bar = style({
|
||||||
border: "2px solid #2d2d2d",
|
border: "2px solid #2d2d2d",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const tinyBar = style({
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
flexDirection: "row",
|
||||||
|
overflow: "hidden",
|
||||||
|
fontSize: 5,
|
||||||
|
});
|
||||||
|
|
||||||
const shared: ComplexStyleRule = {
|
const shared: ComplexStyleRule = {
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import type { Population } from "~/utils/saerro";
|
import type { Population } from "~/utils/saerro";
|
||||||
import { totalPopulation } from "~/utils/saerro";
|
|
||||||
import * as styles from "./faction-bar.css";
|
import * as styles from "./faction-bar.css";
|
||||||
|
|
||||||
export const FactionBar = ({
|
export const FactionBar = ({
|
||||||
population: { vs, nc, tr },
|
population: { vs, nc, tr },
|
||||||
|
tiny,
|
||||||
}: {
|
}: {
|
||||||
population: Population;
|
population: Population;
|
||||||
|
tiny?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { vsPercent, ncPercent, trPercent } = useMemo(() => {
|
const { vsPercent, ncPercent, trPercent } = useMemo(() => {
|
||||||
const total = totalPopulation({ vs, nc, tr, total: 0 });
|
const total = nc + vs + tr;
|
||||||
return {
|
return {
|
||||||
vsPercent: Math.round((vs / total) * 100) || 0,
|
vsPercent: Math.round((vs / total) * 100) || 0,
|
||||||
ncPercent: Math.round((nc / total) * 100) || 0,
|
ncPercent: Math.round((nc / total) * 100) || 0,
|
||||||
|
@ -17,15 +18,15 @@ export const FactionBar = ({
|
||||||
};
|
};
|
||||||
}, [vs, nc, tr]);
|
}, [vs, nc, tr]);
|
||||||
return (
|
return (
|
||||||
<div className={styles.bar}>
|
<div className={tiny ? styles.tinyBar : styles.bar}>
|
||||||
<div className={styles.left} style={{ flexGrow: vs + 1 }}>
|
<div className={styles.left} style={{ flexGrow: vs + 1 }}>
|
||||||
{vsPercent}%
|
{tiny ? <> </> : `${vsPercent}%`}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.center} style={{ flexGrow: nc + 1 }}>
|
<div className={styles.center} style={{ flexGrow: nc + 1 }}>
|
||||||
{ncPercent}%
|
{tiny ? <> </> : `${ncPercent}%`}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.right} style={{ flexGrow: tr + 1 }}>
|
<div className={styles.right} style={{ flexGrow: tr + 1 }}>
|
||||||
{trPercent}%
|
{tiny ? <> </> : `${trPercent}%`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import { useMemo } from "react";
|
|
||||||
import type { Health, World } from "~/utils/saerro";
|
|
||||||
import { IndexWorld } from "./index-world";
|
import { IndexWorld } from "./index-world";
|
||||||
import * as styles from "./index-world-container.css";
|
import * as styles from "./index-world-container.css";
|
||||||
|
import type { MetagameWorld } from "~/utils/metagame";
|
||||||
|
import type { PopulationWorld } from "~/utils/population";
|
||||||
|
|
||||||
export const WorldContainer = ({
|
export const WorldContainer = ({
|
||||||
worlds,
|
metagame,
|
||||||
health,
|
population,
|
||||||
}: {
|
}: {
|
||||||
worlds: World[];
|
metagame: MetagameWorld[];
|
||||||
health: Health;
|
population: PopulationWorld[];
|
||||||
}) => (
|
}) => (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{worlds.map((world) => (
|
{metagame.map((world) => (
|
||||||
<IndexWorld
|
<IndexWorld
|
||||||
key={world.id}
|
key={world.id}
|
||||||
world={world}
|
metagame={world}
|
||||||
health={health.worlds.find((w) => world.name.toLowerCase() === w.name)}
|
population={
|
||||||
|
population.find((p) => p.id === world.id) as PopulationWorld
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { style } from "@vanilla-extract/css";
|
import { keyframes, style } from "@vanilla-extract/css";
|
||||||
|
|
||||||
export const container = style({
|
export const container = style({
|
||||||
background: "#333",
|
background: "#333",
|
||||||
|
@ -7,11 +7,11 @@ export const container = style({
|
||||||
|
|
||||||
"@media": {
|
"@media": {
|
||||||
// under 600px
|
// under 600px
|
||||||
"screen and (max-width: 600px)": {
|
"screen and (max-width: 800px)": {
|
||||||
flexBasis: "100%",
|
flexBasis: "100%",
|
||||||
},
|
},
|
||||||
// between 600px and 1000px
|
// between 600px and 1000px
|
||||||
"screen and (min-width: 600px) and (max-width: 1000px)": {
|
"screen and (min-width: 800px) and (max-width: 1300px)": {
|
||||||
flexBasis: "45%",
|
flexBasis: "45%",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -79,3 +79,68 @@ export const totalPop = style({
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
fontSize: "1.2rem",
|
fontSize: "1.2rem",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const continent = style({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-evenly",
|
||||||
|
padding: "1rem",
|
||||||
|
background: "#222",
|
||||||
|
margin: "0.5rem",
|
||||||
|
borderRadius: "0.4rem",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const contBars = style({
|
||||||
|
flex: 1,
|
||||||
|
paddingLeft: "0.5rem",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const contBarTitle = style({
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: "0.7rem",
|
||||||
|
padding: "0.2rem 0.5rem",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const contCircle = style({
|
||||||
|
height: "2rem",
|
||||||
|
width: "2rem",
|
||||||
|
borderRadius: "50%",
|
||||||
|
background: "linear-gradient(45deg, var(--upper-color), var(--lower-color))",
|
||||||
|
boxShadow: "0 0 0.5rem 0.1rem rgb(var(--lower-color) / 15%)",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const contName = style({
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
fontWeight: "bold",
|
||||||
|
flexDirection: "column",
|
||||||
|
minWidth: "4rem",
|
||||||
|
paddingTop: "0.5rem",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const jaegerConts = style({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
padding: "1rem",
|
||||||
|
justifyContent: "space-evenly",
|
||||||
|
backgroundColor: "#222",
|
||||||
|
borderRadius: "0.4rem",
|
||||||
|
margin: "0.5rem",
|
||||||
|
});
|
||||||
|
|
||||||
|
const alertFade = keyframes({
|
||||||
|
from: {
|
||||||
|
borderColor: "#ff2d2d",
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
borderColor: "#222",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const alertCont = style({
|
||||||
|
border: "2px solid #ff2d2d",
|
||||||
|
animation: `${alertFade} 1s ease-in-out 4 alternate`,
|
||||||
|
});
|
||||||
|
|
|
@ -1,60 +1,126 @@
|
||||||
import { Link } from "@remix-run/react";
|
import { Link } from "@remix-run/react";
|
||||||
import { Health, totalPopulation, World } from "~/utils/saerro";
|
import {
|
||||||
import { humanTimeAgo } from "~/utils/strings";
|
humanTimeAgo,
|
||||||
import { worlds } from "~/utils/worlds";
|
snakeCaseToTitleCase,
|
||||||
|
worlds,
|
||||||
|
zones,
|
||||||
|
} from "~/utils/strings";
|
||||||
import * as styles from "./index-world.css";
|
import * as styles from "./index-world.css";
|
||||||
import vsLogo from "~/images/vs-100.png";
|
import vsLogo from "~/images/vs-100.png";
|
||||||
import ncLogo from "~/images/nc-100.png";
|
import ncLogo from "~/images/nc-100.png";
|
||||||
import trLogo from "~/images/tr-100.png";
|
import trLogo from "~/images/tr-100.png";
|
||||||
import { FactionBar } from "./faction-bar";
|
import { FactionBar } from "./faction-bar";
|
||||||
|
import type { MetagameWorld } from "~/utils/metagame";
|
||||||
|
import type { PopulationWorld } from "~/utils/population";
|
||||||
|
import { c } from "~/utils/classes";
|
||||||
|
|
||||||
export type IndexWorldProps = {
|
export type IndexWorldProps = {
|
||||||
world: World;
|
metagame: MetagameWorld;
|
||||||
health?: Health["worlds"][number];
|
population: PopulationWorld;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IndexWorld = ({ world, health }: IndexWorldProps) => {
|
export const IndexWorld = ({ metagame, population }: IndexWorldProps) => {
|
||||||
const { platform, location } = worlds[String(world.id || "default")];
|
const worldId = metagame.id;
|
||||||
|
const { platform, location, name } = worlds[String(worldId || "default")];
|
||||||
const timeSinceLastEvent = humanTimeAgo(
|
|
||||||
new Date().getTime() - new Date(health?.lastEvent || 0).getTime()
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<Link to={`/worlds/${world.id}`} className={styles.header}>
|
<Link to={`/worlds/${worldId}`} className={styles.header}>
|
||||||
<div className={styles.headerName}>{world.name}</div>
|
<div className={styles.headerName}>{name}</div>
|
||||||
<div className={styles.headerMarkers}>
|
<div className={styles.headerMarkers}>
|
||||||
[{location}] [{platform}]{" "}
|
[{location}] [{platform}]{" "}
|
||||||
<div
|
|
||||||
className={styles.circle}
|
|
||||||
style={{
|
|
||||||
backgroundColor: health?.status === "UP" ? "limegreen" : "red",
|
|
||||||
}}
|
|
||||||
title={`Status: ${health?.status} || Last event: ${timeSinceLastEvent}`}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.headerDetailsLink}>DETAILS ⇨</div>
|
<div className={styles.headerDetailsLink}>DETAILS ⇨</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div className={styles.details}>
|
<div className={styles.details}>
|
||||||
<div className={styles.population}>
|
<div className={styles.population}>
|
||||||
<div className={styles.totalPop}>
|
<div className={styles.totalPop}>
|
||||||
{totalPopulation(world.population)}
|
{population.factions.vs +
|
||||||
|
population.factions.nc +
|
||||||
|
population.factions.tr}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.popFaction}>
|
<div className={styles.popFaction}>
|
||||||
<img className={styles.popImage} src={vsLogo} alt="VS" />{" "}
|
<img className={styles.popImage} src={vsLogo} alt="VS" />{" "}
|
||||||
{world.population.vs}
|
{population.factions.vs}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.popFaction}>
|
<div className={styles.popFaction}>
|
||||||
<img className={styles.popImage} src={ncLogo} alt="NC" />{" "}
|
<img className={styles.popImage} src={ncLogo} alt="NC" />{" "}
|
||||||
{world.population.nc}
|
{population.factions.nc}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.popFaction}>
|
<div className={styles.popFaction}>
|
||||||
<img className={styles.popImage} src={trLogo} alt="TR" />{" "}
|
<img className={styles.popImage} src={trLogo} alt="TR" />{" "}
|
||||||
{world.population.tr}
|
{population.factions.tr}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FactionBar population={world.population} />
|
<FactionBar population={population.factions} />
|
||||||
|
</div>
|
||||||
|
<div className={c(worldId === 19 && styles.jaegerConts)}>
|
||||||
|
{metagame.zones
|
||||||
|
.filter((zone) => !zone.locked)
|
||||||
|
.sort((a, b) => {
|
||||||
|
return a.alert && !b.alert ? -1 : b.alert && !a.alert ? 1 : 0;
|
||||||
|
})
|
||||||
|
.map((zone) => {
|
||||||
|
let {
|
||||||
|
name,
|
||||||
|
colors: [upper, lower],
|
||||||
|
} = zones[zone.id];
|
||||||
|
return worldId !== 19 ? (
|
||||||
|
<div
|
||||||
|
key={zone.id}
|
||||||
|
className={c(styles.continent, zone.alert && styles.alertCont)}
|
||||||
|
>
|
||||||
|
<div className={styles.contName}>
|
||||||
|
<div
|
||||||
|
className={styles.contCircle}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--upper-color": upper,
|
||||||
|
"--lower-color": lower,
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
></div>
|
||||||
|
<div>{name}</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.contBars}>
|
||||||
|
<div>
|
||||||
|
<div className={styles.contBarTitle}>TERRITORY CONTROL</div>
|
||||||
|
<FactionBar population={zone.territory} />
|
||||||
|
</div>
|
||||||
|
{zone.alert && (
|
||||||
|
<div>
|
||||||
|
<div className={styles.contBarTitle}>
|
||||||
|
{snakeCaseToTitleCase(
|
||||||
|
zone.alert.alert_type
|
||||||
|
).toUpperCase()}{" "}
|
||||||
|
ALERT PROGRESS | STARTED{" "}
|
||||||
|
{humanTimeAgo(
|
||||||
|
Date.now() -
|
||||||
|
new Date(zone.alert.start_time).getTime(),
|
||||||
|
true
|
||||||
|
).toUpperCase()}{" "}
|
||||||
|
AGO
|
||||||
|
</div>
|
||||||
|
<FactionBar population={zone.alert.percentages} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div key={zone.id} className={styles.contName}>
|
||||||
|
<div
|
||||||
|
className={styles.contCircle}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--upper-color": upper,
|
||||||
|
"--lower-color": lower,
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
></div>
|
||||||
|
<div>{name}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
8
app/components/index.css.ts
Normal file
8
app/components/index.css.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { style } from "@vanilla-extract/css";
|
||||||
|
|
||||||
|
export const outer = style({
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
minHeight: "100vh",
|
||||||
|
});
|
|
@ -2,10 +2,18 @@ import { json, type V2_MetaFunction } from "@remix-run/cloudflare";
|
||||||
import { useLoaderData } from "@remix-run/react";
|
import { useLoaderData } from "@remix-run/react";
|
||||||
import { IndexWorld } from "~/components/index-world";
|
import { IndexWorld } from "~/components/index-world";
|
||||||
import { WorldContainer } from "~/components/index-world-container";
|
import { WorldContainer } from "~/components/index-world-container";
|
||||||
|
import { outer } from "~/components/index.css";
|
||||||
|
import { fetchMetagameWorlds } from "~/utils/metagame";
|
||||||
|
import { fetchPopulationWorlds } from "~/utils/population";
|
||||||
import { indexQuery } from "~/utils/saerro";
|
import { indexQuery } from "~/utils/saerro";
|
||||||
|
|
||||||
export const loader = async () => {
|
export const loader = async () => {
|
||||||
return json(await indexQuery());
|
const [metagame, population] = await Promise.all([
|
||||||
|
fetchMetagameWorlds(),
|
||||||
|
fetchPopulationWorlds(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return json({ metagame, population });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const meta: V2_MetaFunction = () => {
|
export const meta: V2_MetaFunction = () => {
|
||||||
|
@ -21,10 +29,8 @@ export const meta: V2_MetaFunction = () => {
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
const data = useLoaderData<typeof loader>();
|
const data = useLoaderData<typeof loader>();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={outer}>
|
||||||
<h1>PS2.LIVE</h1>
|
<WorldContainer metagame={data.metagame} population={data.population} />
|
||||||
<h2>Worlds</h2>
|
|
||||||
<WorldContainer worlds={data.allWorlds} health={data.health} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@ import { useLoaderData } from "@remix-run/react";
|
||||||
import type { Zone } from "~/utils/saerro";
|
import type { Zone } from "~/utils/saerro";
|
||||||
import { totalPopulation } from "~/utils/saerro";
|
import { totalPopulation } from "~/utils/saerro";
|
||||||
import { allClasses, allVehicles, worldQuery } from "~/utils/saerro";
|
import { allClasses, allVehicles, worldQuery } from "~/utils/saerro";
|
||||||
import { pascalCaseToTitleCase, toTitleCase } from "~/utils/strings";
|
import { pascalCaseToTitleCase, toTitleCase, worlds } from "~/utils/strings";
|
||||||
import { worlds } from "~/utils/worlds";
|
|
||||||
|
|
||||||
export const loader = async ({ params }: LoaderArgs) => {
|
export const loader = async ({ params }: LoaderArgs) => {
|
||||||
return json(await worldQuery(params.id as string));
|
return json(await worldQuery(params.id as string));
|
||||||
|
|
1
app/utils/classes.ts
Normal file
1
app/utils/classes.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export const c = (...args: any[]) => args.filter((x) => !!x).join(" ");
|
31
app/utils/metagame.ts
Normal file
31
app/utils/metagame.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
export type MetagameWorld = {
|
||||||
|
id: number;
|
||||||
|
zones: {
|
||||||
|
id: number;
|
||||||
|
locked: boolean;
|
||||||
|
alert?: {
|
||||||
|
id: number;
|
||||||
|
zone: number;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
ps2alerts: string;
|
||||||
|
alert_type: string;
|
||||||
|
percentages: {
|
||||||
|
nc: number;
|
||||||
|
tr: number;
|
||||||
|
vs: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
territory: {
|
||||||
|
nc: number;
|
||||||
|
tr: number;
|
||||||
|
vs: number;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchMetagameWorlds = async (): Promise<MetagameWorld[]> => {
|
||||||
|
const response = await fetch("https://metagame.ps2.live/all");
|
||||||
|
const data: MetagameWorld[] = await response.json();
|
||||||
|
return data;
|
||||||
|
};
|
15
app/utils/population.ts
Normal file
15
app/utils/population.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export type PopulationWorld = {
|
||||||
|
id: number;
|
||||||
|
average: number;
|
||||||
|
factions: {
|
||||||
|
nc: number;
|
||||||
|
tr: number;
|
||||||
|
vs: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchPopulationWorlds = async (): Promise<PopulationWorld[]> => {
|
||||||
|
const response = await fetch("https://agg.ps2.live/population/all");
|
||||||
|
const data: PopulationWorld[] = await response.json();
|
||||||
|
return data.map(({ id, average, factions }) => ({ id, average, factions }));
|
||||||
|
};
|
|
@ -12,7 +12,7 @@ export const saerroFetch = async <T>(query: string): Promise<T> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Population = {
|
export type Population = {
|
||||||
total: number;
|
total?: number;
|
||||||
nc: number;
|
nc: number;
|
||||||
tr: number;
|
tr: number;
|
||||||
vs: number;
|
vs: number;
|
||||||
|
@ -22,10 +22,10 @@ export type Zone = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
population: Population;
|
population: Population;
|
||||||
vehicles?: Record<(typeof allVehicles)[number], Population> & {
|
vehicles?: Record<typeof allVehicles[number], Population> & {
|
||||||
total: number;
|
total: number;
|
||||||
};
|
};
|
||||||
classes?: Record<(typeof allClasses)[number], Population>;
|
classes?: Record<typeof allClasses[number], Population>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type World = {
|
export type World = {
|
||||||
|
|
|
@ -8,14 +8,18 @@ export const pascalCaseToTitleCase = (str: string) => {
|
||||||
return toTitleCase(str.replace(/([A-Z])/g, " $1"));
|
return toTitleCase(str.replace(/([A-Z])/g, " $1"));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const humanTimeAgo = (ms: number) => {
|
export const snakeCaseToTitleCase = (str: string) => {
|
||||||
|
return toTitleCase(str.replace(/_/g, " "));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const humanTimeAgo = (ms: number, full?: boolean) => {
|
||||||
const millis = Math.floor(ms % 1000);
|
const millis = Math.floor(ms % 1000);
|
||||||
const seconds = Math.floor(ms / 1000);
|
const seconds = Math.floor(ms / 1000);
|
||||||
const minutes = Math.floor(seconds / 60);
|
const minutes = Math.floor(seconds / 60);
|
||||||
const hours = Math.floor(minutes / 60);
|
const hours = Math.floor(minutes / 60);
|
||||||
|
|
||||||
if (hours > 0) {
|
if (hours > 0) {
|
||||||
return `${hours}h`;
|
return full ? `${hours}h ${minutes % 60}m` : `${hours}h`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minutes > 0) {
|
if (minutes > 0) {
|
||||||
|
@ -28,3 +32,106 @@ export const humanTimeAgo = (ms: number) => {
|
||||||
|
|
||||||
return `${millis}ms`;
|
return `${millis}ms`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const worlds: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
timeZone: string;
|
||||||
|
locale: string;
|
||||||
|
location: string;
|
||||||
|
platform: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
"1": {
|
||||||
|
name: "Connery",
|
||||||
|
timeZone: "America/Los_Angeles",
|
||||||
|
locale: "en-US",
|
||||||
|
location: "US-W",
|
||||||
|
platform: "PC",
|
||||||
|
},
|
||||||
|
"10": {
|
||||||
|
name: "Miller",
|
||||||
|
timeZone: "UTC",
|
||||||
|
locale: "en-GB",
|
||||||
|
location: "EU",
|
||||||
|
platform: "PC",
|
||||||
|
},
|
||||||
|
"13": {
|
||||||
|
name: "Cobalt",
|
||||||
|
timeZone: "UTC",
|
||||||
|
locale: "en-GB",
|
||||||
|
location: "EU",
|
||||||
|
platform: "PC",
|
||||||
|
},
|
||||||
|
"17": {
|
||||||
|
name: "Emerald",
|
||||||
|
timeZone: "America/New_York",
|
||||||
|
locale: "en-US",
|
||||||
|
location: "US-E",
|
||||||
|
platform: "PC",
|
||||||
|
},
|
||||||
|
"19": {
|
||||||
|
name: "Jaeger",
|
||||||
|
timeZone: "America/New_York",
|
||||||
|
locale: "en-US",
|
||||||
|
location: "US-E",
|
||||||
|
platform: "PC",
|
||||||
|
},
|
||||||
|
"40": {
|
||||||
|
name: "SolTech",
|
||||||
|
timeZone: "Asia/Tokyo",
|
||||||
|
locale: "en-GB",
|
||||||
|
location: "JP",
|
||||||
|
platform: "PC",
|
||||||
|
},
|
||||||
|
"1000": {
|
||||||
|
name: "Genudine",
|
||||||
|
timeZone: "America/New_York",
|
||||||
|
locale: "en-US",
|
||||||
|
location: "US-E",
|
||||||
|
platform: "PS4",
|
||||||
|
},
|
||||||
|
"2000": {
|
||||||
|
name: "Ceres",
|
||||||
|
timeZone: "UTC",
|
||||||
|
locale: "en-GB",
|
||||||
|
location: "EU",
|
||||||
|
platform: "PS4",
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
name: "Unknown",
|
||||||
|
timeZone: "UTC",
|
||||||
|
locale: "en-US",
|
||||||
|
location: "???",
|
||||||
|
platform: "???",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const zones: Record<string, { name: string; colors: [string, string] }> =
|
||||||
|
{
|
||||||
|
"2": {
|
||||||
|
name: "Indar",
|
||||||
|
colors: ["#edb96b", "#964c2f"],
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
name: "Hossin",
|
||||||
|
colors: ["#47570d", "#7b9c05"],
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
name: "Amerish",
|
||||||
|
colors: ["#87a12a", "#5f634f"],
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
name: "Esamir",
|
||||||
|
colors: ["#d5f3f5", "#a1c7e6"],
|
||||||
|
},
|
||||||
|
"344": {
|
||||||
|
name: "Oshur",
|
||||||
|
colors: ["#00c2bf", "#174185"],
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
name: "Unknown",
|
||||||
|
colors: ["#000000", "#000000"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
export const worlds: Record<
|
|
||||||
string,
|
|
||||||
{ timeZone: string; locale: string; location: string; platform: string }
|
|
||||||
> = {
|
|
||||||
"1": {
|
|
||||||
timeZone: "America/Los_Angeles",
|
|
||||||
locale: "en-US",
|
|
||||||
location: "US-W",
|
|
||||||
platform: "PC",
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
timeZone: "UTC",
|
|
||||||
locale: "en-GB",
|
|
||||||
location: "EU",
|
|
||||||
platform: "PC",
|
|
||||||
},
|
|
||||||
"13": {
|
|
||||||
timeZone: "UTC",
|
|
||||||
locale: "en-GB",
|
|
||||||
location: "EU",
|
|
||||||
platform: "PC",
|
|
||||||
},
|
|
||||||
"17": {
|
|
||||||
timeZone: "America/New_York",
|
|
||||||
locale: "en-US",
|
|
||||||
location: "US-E",
|
|
||||||
platform: "PC",
|
|
||||||
},
|
|
||||||
"19": {
|
|
||||||
timeZone: "America/New_York",
|
|
||||||
locale: "en-US",
|
|
||||||
location: "US-E",
|
|
||||||
platform: "PC",
|
|
||||||
},
|
|
||||||
"40": {
|
|
||||||
timeZone: "Asia/Tokyo",
|
|
||||||
locale: "en-GB",
|
|
||||||
location: "JP",
|
|
||||||
platform: "PC",
|
|
||||||
},
|
|
||||||
"1000": {
|
|
||||||
timeZone: "America/New_York",
|
|
||||||
locale: "en-US",
|
|
||||||
location: "US-E",
|
|
||||||
platform: "PS4",
|
|
||||||
},
|
|
||||||
"2000": {
|
|
||||||
timeZone: "UTC",
|
|
||||||
locale: "en-GB",
|
|
||||||
location: "EU",
|
|
||||||
platform: "PS4",
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
timeZone: "UTC",
|
|
||||||
locale: "en-US",
|
|
||||||
location: "???",
|
|
||||||
platform: "???",
|
|
||||||
},
|
|
||||||
};
|
|
Loading…
Add table
Add a link
Reference in a new issue