Strongly type everything
This commit is contained in:
parent
842a627cac
commit
fee3766c2b
18 changed files with 221 additions and 109 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { Handler } from '@netlify/functions'
|
import { Handler } from '@netlify/functions'
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
|
import { AnilistInfo } from '../../src/components/Anilist'
|
||||||
|
|
||||||
const handler: Handler = async (event, context) => {
|
const handler: Handler = async (event, context) => {
|
||||||
let anilist = await fetch("https://graphql.anilist.co", {
|
let anilist = await fetch("https://graphql.anilist.co", {
|
||||||
|
@ -52,7 +53,7 @@ const handler: Handler = async (event, context) => {
|
||||||
|
|
||||||
let p_json = await anilist.json() as {[key: string]: any}
|
let p_json = await anilist.json() as {[key: string]: any}
|
||||||
let json = p_json.data.MediaList
|
let json = p_json.data.MediaList
|
||||||
let anime = {
|
let anime: AnilistInfo = {
|
||||||
title: json.media.title.romaji,
|
title: json.media.title.romaji,
|
||||||
episodes: {
|
episodes: {
|
||||||
watched: json.progress,
|
watched: json.progress,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Handler } from '@netlify/functions'
|
import { Handler } from '@netlify/functions'
|
||||||
import { Octokit } from '@octokit/core'
|
import { Octokit } from '@octokit/core'
|
||||||
|
import { GithubInfo } from '../../src/components/Github'
|
||||||
|
|
||||||
const handler: Handler = async (event, context) => {
|
const handler: Handler = async (event, context) => {
|
||||||
let octokit = new Octokit({auth: process.env.API_GITHUB})
|
let octokit = new Octokit({auth: process.env.API_GITHUB})
|
||||||
|
@ -20,7 +21,7 @@ const handler: Handler = async (event, context) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = {
|
let info: GithubInfo = {
|
||||||
public: {
|
public: {
|
||||||
repo: public_push.repo.name,
|
repo: public_push.repo.name,
|
||||||
date: public_push.created_at.substring(0, public_push.created_at.indexOf("T"))
|
date: public_push.created_at.substring(0, public_push.created_at.indexOf("T"))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Handler } from '@netlify/functions'
|
import { Handler } from '@netlify/functions'
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
|
import { GitlabInfo } from '../../src/components/Gitlab'
|
||||||
|
|
||||||
const handler: Handler = async (event, context) => {
|
const handler: Handler = async (event, context) => {
|
||||||
let gitlab = await fetch("https://gitlab.com/api/v4/events?action=pushed", {
|
let gitlab = await fetch("https://gitlab.com/api/v4/events?action=pushed", {
|
||||||
|
@ -19,11 +20,13 @@ const handler: Handler = async (event, context) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let json = await gitlab.json() as {[key: string]: any}
|
let json = await gitlab.json() as {[key: string]: any}
|
||||||
let date = json[0].created_at.substring(0, json[0].created_at.indexOf("T"))
|
let activity: GitlabInfo = {
|
||||||
|
date: json[0].created_at.substring(0, json[0].created_at.indexOf("T"))
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: JSON.stringify({date})
|
body: JSON.stringify(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { Handler } from '@netlify/functions'
|
import { Handler } from '@netlify/functions'
|
||||||
import { api } from "./shared/api"
|
import { api } from "./shared/api"
|
||||||
|
import { HacktheboxInfo } from '../../src/components/Hackthebox'
|
||||||
|
|
||||||
const handler: Handler = async (event, context) => {
|
const handler: Handler = async (event, context) => {
|
||||||
let hackthebox = await api<{
|
let hackthebox: {profile: {activity: HacktheboxInfo[]}} = await api<{
|
||||||
profile: {
|
profile: {
|
||||||
activity: {
|
activity: {
|
||||||
id: string
|
id: string
|
||||||
|
@ -16,7 +17,7 @@ const handler: Handler = async (event, context) => {
|
||||||
}>
|
}>
|
||||||
(`https://www.hackthebox.com/api/v4/profile/activity/1063999`)
|
(`https://www.hackthebox.com/api/v4/profile/activity/1063999`)
|
||||||
|
|
||||||
let pwn = hackthebox.profile.activity.find((a) => a.object_type === "machine")
|
let pwn = hackthebox.profile.activity.find((a: HacktheboxInfo) => a!.object_type === "machine")
|
||||||
if (!pwn) {
|
if (!pwn) {
|
||||||
return {
|
return {
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Handler } from '@netlify/functions'
|
import { Handler } from '@netlify/functions'
|
||||||
import { API, APIError, User } from 'osu-api-v2-js'
|
import { API, APIError, User } from 'osu-api-v2-js'
|
||||||
|
import { OsuInfo } from '../../src/components/Osu'
|
||||||
|
|
||||||
const handler: Handler = async (event, context) => {
|
const handler: Handler = async (event, context) => {
|
||||||
let api = await API.createAsync({id: 11451, secret: process.env.API_OSU!})
|
let api = await API.createAsync({id: 11451, secret: process.env.API_OSU!})
|
||||||
|
@ -24,7 +25,7 @@ const handler: Handler = async (event, context) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ranks = {
|
let ranks: OsuInfo = {
|
||||||
osu: [0,0],
|
osu: [0,0],
|
||||||
taiko: [0,0],
|
taiko: [0,0],
|
||||||
fruits: [0,0],
|
fruits: [0,0],
|
||||||
|
|
BIN
public/mode-fruits.png
Normal file
BIN
public/mode-fruits.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
public/mode-mania.png
Normal file
BIN
public/mode-mania.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
public/mode-osu.png
Normal file
BIN
public/mode-osu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
public/mode-taiko.png
Normal file
BIN
public/mode-taiko.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -50,7 +50,8 @@ body {
|
||||||
writing-mode: vertical-rl;
|
writing-mode: vertical-rl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info > * {
|
.info canvas {
|
||||||
padding: 3px 10px;
|
height: 110px !important;
|
||||||
margin: auto;
|
width: 220px !important;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
12
src/App.jsx
12
src/App.jsx
|
@ -3,11 +3,11 @@ import "./App.css";
|
||||||
|
|
||||||
import Lastfm from "./components/Lastfm";
|
import Lastfm from "./components/Lastfm";
|
||||||
import Speedruncom from "./components/Speedruncom";
|
import Speedruncom from "./components/Speedruncom";
|
||||||
import Hackthebox from "./components/hackthebox";
|
import Hackthebox from "./components/Hackthebox";
|
||||||
import Github from "./components/github";
|
import Github from "./components/Github";
|
||||||
import Gitlab from "./components/gitlab";
|
import Gitlab from "./components/Gitlab";
|
||||||
import Osu from "./components/osu";
|
import Osu from "./components/Osu";
|
||||||
import Anilist from "./components/anilist";
|
import Anilist from "./components/Anilist";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -16,10 +16,10 @@ function App() {
|
||||||
<div className="info_container">
|
<div className="info_container">
|
||||||
<Lastfm/>
|
<Lastfm/>
|
||||||
<Speedruncom/>
|
<Speedruncom/>
|
||||||
|
<Osu/>
|
||||||
{/* <Hackthebox/>
|
{/* <Hackthebox/>
|
||||||
<Github/>
|
<Github/>
|
||||||
<Gitlab/>
|
<Gitlab/>
|
||||||
<Osu/>
|
|
||||||
<Anilist/> */}
|
<Anilist/> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,20 @@
|
||||||
import { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export type AnilistInfo = {
|
||||||
|
title: string
|
||||||
|
episodes: {
|
||||||
|
watched: number
|
||||||
|
total: number
|
||||||
|
},
|
||||||
|
score: number
|
||||||
|
startDate: string
|
||||||
|
updateDate: string
|
||||||
|
endDate: string
|
||||||
|
cover: string
|
||||||
|
} | undefined
|
||||||
|
|
||||||
export default function Anilist() {
|
export default function Anilist() {
|
||||||
const [anilist, setAnilist] = useState({})
|
const [anilist, setAnilist]: [AnilistInfo, React.Dispatch<React.SetStateAction<AnilistInfo>>] = useState()
|
||||||
const getAnilist = async () => {
|
const getAnilist = async () => {
|
||||||
const response = await fetch("/.netlify/functions/anilist").then(r => r.json())
|
const response = await fetch("/.netlify/functions/anilist").then(r => r.json())
|
||||||
setAnilist(response)
|
setAnilist(response)
|
||||||
|
@ -11,7 +24,7 @@ export default function Anilist() {
|
||||||
getAnilist()
|
getAnilist()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (anilist.title === undefined) {
|
if (anilist === undefined) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return (
|
return (
|
|
@ -1,7 +1,17 @@
|
||||||
import { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export type GithubInfo = {
|
||||||
|
public: {
|
||||||
|
repo: string
|
||||||
|
date: string
|
||||||
|
}
|
||||||
|
private: {
|
||||||
|
date: string
|
||||||
|
}
|
||||||
|
} | undefined
|
||||||
|
|
||||||
export default function Github() {
|
export default function Github() {
|
||||||
const [github, setGithub] = useState({})
|
const [github, setGithub]: [GithubInfo, React.Dispatch<React.SetStateAction<GithubInfo>>] = useState()
|
||||||
const getGithub = async () => {
|
const getGithub = async () => {
|
||||||
const response = await fetch("/.netlify/functions/github").then(r => r.json())
|
const response = await fetch("/.netlify/functions/github").then(r => r.json())
|
||||||
setGithub(response)
|
setGithub(response)
|
||||||
|
@ -11,7 +21,7 @@ export default function Github() {
|
||||||
getGithub()
|
getGithub()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (github.public === undefined || github.public.date === undefined) {
|
if (github === undefined) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return (
|
return (
|
|
@ -1,7 +1,11 @@
|
||||||
import { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export type GitlabInfo = {
|
||||||
|
date: string
|
||||||
|
} | undefined
|
||||||
|
|
||||||
export default function Gitlab() {
|
export default function Gitlab() {
|
||||||
const [gitlab, setGitlab] = useState({})
|
const [gitlab, setGitlab]: [GitlabInfo, React.Dispatch<React.SetStateAction<GitlabInfo>>] = useState()
|
||||||
const getGitlab = async () => {
|
const getGitlab = async () => {
|
||||||
const response = await fetch("/.netlify/functions/gitlab").then(r => r.json())
|
const response = await fetch("/.netlify/functions/gitlab").then(r => r.json())
|
||||||
setGitlab(response)
|
setGitlab(response)
|
||||||
|
@ -11,7 +15,7 @@ export default function Gitlab() {
|
||||||
getGitlab()
|
getGitlab()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (gitlab.date === undefined) {
|
if (gitlab === undefined) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
return (
|
return (
|
|
@ -1,7 +1,16 @@
|
||||||
import { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export type HacktheboxInfo = {
|
||||||
|
id: string
|
||||||
|
date_diff: string
|
||||||
|
object_type: string
|
||||||
|
type: string
|
||||||
|
name: string
|
||||||
|
machine_avatar: string
|
||||||
|
} | undefined
|
||||||
|
|
||||||
export default function Hackthebox() {
|
export default function Hackthebox() {
|
||||||
const [hackthebox, setHackthebox] = useState({})
|
const [hackthebox, setHackthebox]: [HacktheboxInfo, React.Dispatch<React.SetStateAction<HacktheboxInfo>>] = useState()
|
||||||
const getHackthebox = async () => {
|
const getHackthebox = async () => {
|
||||||
const response = await fetch("/.netlify/functions/hackthebox").then(r => r.json())
|
const response = await fetch("/.netlify/functions/hackthebox").then(r => r.json())
|
||||||
setHackthebox(response)
|
setHackthebox(response)
|
||||||
|
@ -11,7 +20,7 @@ export default function Hackthebox() {
|
||||||
getHackthebox()
|
getHackthebox()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (hackthebox.name === undefined) {
|
if (hackthebox === undefined) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
126
src/components/Osu.tsx
Normal file
126
src/components/Osu.tsx
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
} from 'chart.js';
|
||||||
|
import { Line } from 'react-chartjs-2';
|
||||||
|
import Info from "./structures/info";
|
||||||
|
import "../style/osu.css"
|
||||||
|
|
||||||
|
export type OsuInfo = {
|
||||||
|
osu: number[]
|
||||||
|
taiko: number[]
|
||||||
|
fruits: number[]
|
||||||
|
mania: number[]
|
||||||
|
} | undefined
|
||||||
|
|
||||||
|
ChartJS.register(
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function Osu() {
|
||||||
|
const [osu, setOsu]: [OsuInfo, React.Dispatch<React.SetStateAction<OsuInfo>>] = useState()
|
||||||
|
const getOsu = async () => {
|
||||||
|
const response = await fetch("/.netlify/functions/osu").then(r => r.json())
|
||||||
|
setOsu(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getOsu()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (osu === undefined) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
function shapeData(ranks: number[]) {
|
||||||
|
let labels = ranks.map((r, i) => `${i + 1} days ago`).reverse()
|
||||||
|
return {
|
||||||
|
labels,
|
||||||
|
datasets: [{
|
||||||
|
id: 1,
|
||||||
|
label: "",
|
||||||
|
data: ranks,
|
||||||
|
borderColor: `rgb(
|
||||||
|
${String(ranks[0]).slice(-2)},
|
||||||
|
${String(ranks[ranks.length/2]).slice(-2)},
|
||||||
|
${String(ranks[ranks.length-1]).slice(-2)}
|
||||||
|
)`,
|
||||||
|
tension: 0.5,
|
||||||
|
fill: false,
|
||||||
|
pointStyle: false as false
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
reverse: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Info
|
||||||
|
title="osu!"
|
||||||
|
link="https://osu.ppy.sh/users/7276846"
|
||||||
|
description="Rhythm games"
|
||||||
|
elements={[
|
||||||
|
<div>
|
||||||
|
<a href="https://osu.ppy.sh/users/7276846/osu" target="_blank" className="gamemode-title">
|
||||||
|
<img src="mode-osu.png"></img>
|
||||||
|
<h2><strong>osu!</strong></h2>
|
||||||
|
</a>
|
||||||
|
<div className="bg-white">
|
||||||
|
<Line datasetIdKey="1" data={shapeData(osu.osu)} options={options}/>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
<div className="mt-6">
|
||||||
|
<a href="https://osu.ppy.sh/users/7276846/taiko" target="_blank" className="gamemode-title">
|
||||||
|
<img src="mode-taiko.png"></img>
|
||||||
|
<h2>osu!<strong>taiko</strong></h2>
|
||||||
|
</a>
|
||||||
|
<div className="bg-white">
|
||||||
|
<Line datasetIdKey="1" data={shapeData(osu.taiko)} options={options}/>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
<div className="mt-6">
|
||||||
|
<a href="https://osu.ppy.sh/users/7276846/fruits" target="_blank" className="gamemode-title">
|
||||||
|
<img src="mode-fruits.png"></img>
|
||||||
|
<h2>osu!<strong>catch</strong></h2>
|
||||||
|
</a>
|
||||||
|
<div className="bg-white">
|
||||||
|
<Line datasetIdKey="1" data={shapeData(osu.fruits)} options={options}/>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
<div className="mt-6">
|
||||||
|
<a href="https://osu.ppy.sh/users/7276846/mania" target="_blank" className="gamemode-title">
|
||||||
|
<img src="mode-mania.png"></img>
|
||||||
|
<h2>osu!<strong>mania</strong></h2>
|
||||||
|
</a>
|
||||||
|
<div className="bg-white">
|
||||||
|
<Line datasetIdKey="1" data={shapeData(osu.mania)} options={options}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,81 +0,0 @@
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import {
|
|
||||||
Chart as ChartJS,
|
|
||||||
CategoryScale,
|
|
||||||
LinearScale,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Title,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
scales,
|
|
||||||
} from 'chart.js';
|
|
||||||
import { Line } from 'react-chartjs-2';
|
|
||||||
|
|
||||||
ChartJS.register(
|
|
||||||
CategoryScale,
|
|
||||||
LinearScale,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Title,
|
|
||||||
Tooltip,
|
|
||||||
Legend
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function Osu() {
|
|
||||||
const [osu, setOsu] = useState({})
|
|
||||||
const getOsu = async () => {
|
|
||||||
const response = await fetch("/.netlify/functions/osu").then(r => r.json())
|
|
||||||
setOsu(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getOsu()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (osu.osu === undefined) {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
function shapeData(ranks) {
|
|
||||||
let labels = ranks.map((r, i) => `${i + 1} days ago`).reverse()
|
|
||||||
return {
|
|
||||||
labels,
|
|
||||||
datasets: [{
|
|
||||||
id: 1,
|
|
||||||
label: "",
|
|
||||||
data: ranks,
|
|
||||||
borderColor: `rgb(
|
|
||||||
${String(ranks[0]).slice(-2)},
|
|
||||||
${String(ranks[ranks.length/2]).slice(-2)},
|
|
||||||
${String(ranks[ranks.length-1]).slice(-2)}
|
|
||||||
)`,
|
|
||||||
tension: 0.5,
|
|
||||||
fill: false,
|
|
||||||
pointStyle: false
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = {
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
reverse: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="osu">
|
|
||||||
<Line datasetIdKey="1" data={shapeData(osu.osu)} options={options}/>
|
|
||||||
<Line datasetIdKey="1" data={shapeData(osu.taiko)} options={options}/>
|
|
||||||
<Line datasetIdKey="1" data={shapeData(osu.fruits)} options={options}/>
|
|
||||||
<Line datasetIdKey="1" data={shapeData(osu.mania)} options={options}/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
.gamemode-title {
|
||||||
|
display: flex;
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamemode-title img {
|
||||||
|
height: 64px;
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamemode-title h2 {
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
font-size: 24px;
|
||||||
|
padding-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gamemode-title > * {
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue