manage and cleanup some types

This commit is contained in:
3zachm 2023-01-28 01:46:11 -08:00
parent c7766aeda3
commit 1393df6fd0
9 changed files with 175 additions and 141 deletions

6
interfaces/APIError.ts Normal file
View file

@ -0,0 +1,6 @@
export default interface APIError {
error: {
message: string;
code: number;
};
}

5
interfaces/UserAsset.ts Normal file
View file

@ -0,0 +1,5 @@
export default interface UserAsset {
name: string;
count: number;
provider: "7tv" | "bttv" | "ffz" | "twitch";
}

5
interfaces/UserBadge.ts Normal file
View file

@ -0,0 +1,5 @@
export default interface UserBadge {
name: string;
color: string;
priority: number;
}

View file

@ -0,0 +1,12 @@
import UserAsset from "./UserAsset";
import UserBadge from "./UserBadge";
export default interface UserFakeDataEntry {
id: number;
name: string;
points: number;
daily_change: number;
daily_change_percent: number;
assets: UserAsset[];
badges: UserBadge[];
}

View file

@ -0,0 +1,5 @@
import UserJSONEntry from "./UserJSONEntry";
export default interface UserFakeDataJSON {
data: UserJSONEntry[];
}

View file

@ -0,0 +1,8 @@
import UserFakeDataEntry from "./UserFakeDataEntry";
export default interface UserJSONEntry extends UserFakeDataEntry {
net_worth: number;
shares: number;
avatar_url: string;
rank: number;
}

View file

@ -1,4 +1,7 @@
import type { NextApiRequest, NextApiResponse } from "next";
import UserBadge from "../../interfaces/UserBadge";
import UserFakeDataEntry from "../../interfaces/UserFakeDataEntry";
import UserJSONEntry from "../../interfaces/UserJSONEntry";
import { createRedisInstance } from "../../misc/redis";
import { getUserByName } from "../../misc/TwitchAPI";
import { fakePrices } from "./fakePrices";
@ -23,43 +26,14 @@ export default async function handler(
return;
}
let data = fakeData;
// calculate all net worths
data = data.map((user) => {
return {
...user,
net_worth:
user.points +
user.assets.reduce(
(a, b) => a + b.count * (fakePrices[b.name] ?? 0),
0
),
};
});
// calculate ranking based on net worth
data = data.sort((a, b) => (b.net_worth ?? 0) - (a.net_worth ?? 0));
data = data.map((user, i) => {
return {
...user,
rank: i + 1,
// calculate total assets held (shares)
shares: user.assets.reduce((a, b) => a + b.count, 0),
// sort users badges by priority
badges: (user.badges ?? []).sort((a, b) => b.priority - a.priority ?? 0),
// sort users assets by total value
assets: user.assets.sort(
(a, b) =>
(fakePrices[b.name] ?? 0) * b.count -
(fakePrices[a.name] ?? 0) * a.count
),
};
});
let userJSON: UserJSONEntry[];
let userList: UserFakeDataEntry[] = fakeData;
let avatarURL = "/img/logo.webp"; // default avatar
// if username is specified, only return that user
if (username) {
// if user does not exist, return error
data = data.filter((u) => u.name === username);
if (data.length === 0) {
// filter for user, add required types
userList = userList.filter((u) => u.name === username);
if (userList.length === 0) {
res
.status(404)
.json({ error: { message: "User not found", code: 20000 } });
@ -80,92 +54,95 @@ export default async function handler(
// temp who cares
twitchData.data[0] = {};
twitchData.data[0].profile_image_url = "/img/logo.webp";
} else {
avatarURL = twitchData.data[0].profile_image_url;
}
// add users profile picture url
data = data.map((u) => {
}
userJSON = userList.map((user) => {
return {
...u,
avatar_url: twitchData.data[0].profile_image_url ?? "",
...user,
// calculate total assets held (shares)
shares: user.assets.reduce((a, b) => a + b.count, 0),
// sort users badges by priority
badges: (user.badges ?? []).sort((a, b) => b.priority - a.priority ?? 0),
avatar_url: avatarURL,
rank: 0,
// sort users assets by total value
assets: user.assets.sort(
(a, b) =>
(fakePrices[b.name] ?? 0) * b.count -
(fakePrices[a.name] ?? 0) * a.count
),
// calculate net worth
net_worth:
user.points +
user.assets.reduce(
(a, b) => a + b.count * (fakePrices[b.name] ?? 0),
0
),
};
});
res.status(200).json({ data: data[0] });
return;
}
// calculate ranking based on net worth
userJSON = userJSON.sort((a, b) => (b.net_worth ?? 0) - (a.net_worth ?? 0));
userJSON = userJSON.map((u, i) => {
return {
...u,
rank: i + 1,
};
});
if (sortBy) {
if (sortBy === "daily_change") {
data = data.sort((a, b) => b.daily_change - a.daily_change);
userJSON = userJSON.sort((a, b) => b.daily_change - a.daily_change);
} else if (sortBy === "daily_change_percent") {
data = data.sort(
userJSON = userJSON.sort(
(a, b) => b.daily_change_percent - a.daily_change_percent
);
} else if (sortBy === "shares") {
data = data.sort((a, b) => (b.shares ?? 0) - (a.shares ?? 0));
userJSON = userJSON.sort((a, b) => (b.shares ?? 0) - (a.shares ?? 0));
} else if (sortBy === "points") {
data = data.sort((a, b) => b.points - a.points);
userJSON = userJSON.sort((a, b) => b.points - a.points);
} else if (sortBy === "name") {
data = data.sort((a, b) => a.name.localeCompare(b.name));
userJSON = userJSON.sort((a, b) => a.name.localeCompare(b.name));
}
if (sortAsc === "true") {
// slow but only needed for temporary fake data anyway
data = data.reverse();
userJSON = userJSON.reverse();
}
}
// fake loading time
await new Promise((resolve) =>
setTimeout(resolve, 250 + Math.random() * 1000)
);
res.status(200).json({ data: data });
res.status(200).json({ data: userJSON });
}
interface asset {
name: string;
count: number;
provider: "7tv" | "bttv" | "ffz" | "twitch";
}
interface fakeDataEntry {
id: number;
name: string;
points: number;
daily_change: number;
daily_change_percent: number;
assets: asset[];
net_worth?: number;
shares?: number;
avatar_url?: string;
badges?: badge[];
}
interface badge {
name: string;
color: string;
priority: number;
}
const adminBadge: badge = {
const adminBadge: UserBadge = {
name: "Admin",
color: "#CC3333",
priority: 99999,
};
const CEOBadge: badge = {
const CEOBadge: UserBadge = {
name: "CEO",
color: "#F97316",
priority: 100000,
};
const webDevBadge: badge = {
const webDevBadge: UserBadge = {
name: "Web Dev",
color: "#a855f7",
priority: 50000,
};
const botDevBadge: badge = {
const botDevBadge: UserBadge = {
name: "Bot Dev",
color: "#48b2f1",
priority: 50001,
};
const fakeData: fakeDataEntry[] = [
const fakeData: UserFakeDataEntry[] = [
{
id: 4,
name: "3zachm",
@ -384,6 +361,7 @@ const fakeData: fakeDataEntry[] = [
provider: "7tv",
},
],
badges: [],
},
{
id: 6,
@ -423,6 +401,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 7,
@ -462,6 +441,7 @@ const fakeData: fakeDataEntry[] = [
provider: "ffz",
},
],
badges: [],
},
{
id: 8,
@ -496,6 +476,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 9,
@ -535,6 +516,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 10,
@ -574,6 +556,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 11,
@ -613,6 +596,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 12,
@ -657,6 +641,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 13,
@ -686,6 +671,7 @@ const fakeData: fakeDataEntry[] = [
provider: "7tv",
},
],
badges: [],
},
{
id: 14,
@ -720,6 +706,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 15,
@ -754,6 +741,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 16,
@ -788,6 +776,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 17,
@ -822,6 +811,7 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
{
id: 18,
@ -856,7 +846,6 @@ const fakeData: fakeDataEntry[] = [
provider: "twitch",
},
],
badges: [],
},
];
export type { fakeDataEntry };

View file

@ -2,6 +2,7 @@ import { m, Variants } from "framer-motion";
import Link from "next/link";
import { ReactElement, useEffect, useState } from "react";
import Loading from "../../components/common/Loading";
import UserJSONEntry from "../../interfaces/UserJSONEntry";
import DashLayout from "../../layouts/DashLayout";
function Ranking() {
@ -158,8 +159,7 @@ function Ranking() {
>
{
// generate table rows
fakeData.map(
(entry: { [key: string]: any }, index: number) => {
fakeData.map((entry: UserJSONEntry, index: number) => {
// if daily change is negative, make it red
let changeClass = " text-lime-500";
if (entry.daily_change_percent < 0) {
@ -191,14 +191,12 @@ function Ranking() {
</h1>
<h1 className={changeClass}>
{(
Math.round(entry.daily_change_percent * 1000) /
10
Math.round(entry.daily_change_percent * 1000) / 10
).toFixed(1) + "%"}
</h1>
</m.div>
);
}
)
})
}
</m.div>
)

View file

@ -5,6 +5,8 @@ import DashLayout from "../../../layouts/DashLayout";
import Image from "next/image";
import Loading from "../../../components/common/Loading";
import { GetServerSideProps } from "next";
import UserJSONEntry from "../../../interfaces/UserJSONEntry";
import APIError from "../../../interfaces/APIError";
interface EmoteURLs {
"7tv": { [key: string]: string };
@ -15,7 +17,8 @@ interface EmoteURLs {
}
interface UserPageProps {
userData: { [key: string]: any };
userData: UserJSONEntry;
serverError: APIError | null;
}
function UserPage(props: UserPageProps) {
@ -28,8 +31,9 @@ function UserPage(props: UserPageProps) {
useEffect(() => {
if (!router.isReady) return;
if (props.userData.error) {
setErrorCode(props.userData.error.code);
// if it is of
if (props.serverError) {
setErrorCode(props.serverError.error.code);
}
fetch("/api/emotes")
.then((res) => res.json())
@ -342,18 +346,15 @@ function UserPage(props: UserPageProps) {
<h1>Shares</h1>
<h1>{props.userData.shares.toLocaleString("en-US")}</h1>
<h1>Trades</h1>
<h1>{(props.userData.trades ?? 0).toLocaleString("en-US")}</h1>
<h1>{(0).toLocaleString("en-US")}</h1>
<h1>Peak rank</h1>
<h1>{(props.userData.peak_rank ?? 0).toLocaleString("en-US")}</h1>
<h1>{(0).toLocaleString("en-US")}</h1>
<h1>Joined</h1>
<h1>
{new Date(props.userData.joined ?? 0).toLocaleDateString(
"en-US",
{
{new Date(0).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
}
)}
})}
</h1>
</m.div>
{/* User's Favorite Emote */}
@ -565,25 +566,30 @@ export const getServerSideProps: GetServerSideProps<UserPageProps> = async (
`/api/fakeUsers?u=${context.query.username}`,
process.env.NEXT_PUBLIC_URL
);
// TODO: add error handling
const res = await fetch(url);
let user = await res.json();
// return error in user.data if user not found
if (user.error) {
user = { data: user };
return {
props: {
userData: user,
serverError: user,
},
};
}
return { props: { userData: user.data } };
return { props: { userData: user.data[0], serverError: null } };
};
UserPage.getLayout = function getLayout(page: ReactElement) {
const { userData } = page.props;
const { userData, serverError } = page.props;
const metaTags = {
title: !userData.error
title: !serverError
? `${userData.name ?? "User 404"} - toffee`
: "User 404 - toffee",
description: !userData.error
description: !serverError
? `${userData.name}'s portfolio on toffee`
: "Couldn't find that user on toffee... :(",
imageUrl: !userData.error ? userData.avatar_url : undefined,
imageUrl: !serverError ? userData.avatar_url : undefined,
misc: {
"twitter:card": "summary",
},