remove react
This commit is contained in:
parent
98412adab9
commit
0bb40ff89f
29 changed files with 430 additions and 1730 deletions
52
src/components/Avatar.astro
Normal file
52
src/components/Avatar.astro
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
import { Image } from "astro:assets"
|
||||
import logo from "@assets/logo.png"
|
||||
---
|
||||
|
||||
<div class="avatar-wrapper">
|
||||
<Image
|
||||
alt="My profile picture"
|
||||
src={logo}
|
||||
class="avatar"
|
||||
width={80}
|
||||
height={80}
|
||||
aria-hidden
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.avatar-wrapper {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
clip-path: var(--clip-avatar-outer);
|
||||
background-color: var(--overlay0);
|
||||
}
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
clip-path: var(--clip-avatar-inner);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const colours = {
|
||||
"online": "var(--green)",
|
||||
"idle": "var(--yellow)",
|
||||
"dnd": "var(--red)",
|
||||
"offline": "var(--overlay0)"
|
||||
};
|
||||
const avatarWrapper: HTMLElement = document.querySelector(".avatar-wrapper");
|
||||
|
||||
window.addEventListener('lanyard-update', (e: CustomEvent) => {
|
||||
const presence = e.detail;
|
||||
avatarWrapper.animate(
|
||||
[
|
||||
{ backgroundColor: colours[presence.discord_status] ?? colours.offline }
|
||||
],
|
||||
{
|
||||
duration: 100,
|
||||
fill: "forwards"
|
||||
},
|
||||
);
|
||||
});
|
||||
</script>
|
|
@ -2,7 +2,6 @@
|
|||
const { ...attrs } = Astro.props
|
||||
---
|
||||
<a {...attrs}>
|
||||
<slot name="icon" />
|
||||
<slot />
|
||||
</a>
|
||||
<style>
|
|
@ -1,7 +1,3 @@
|
|||
---
|
||||
const { name } = Astro.props;
|
||||
---
|
||||
|
||||
<style>
|
||||
.group-box-wrapper {
|
||||
background: linear-gradient(#343343, #2E2E3E, #2E2E3E);
|
||||
|
@ -19,7 +15,6 @@ const { name } = Astro.props;
|
|||
|
||||
<div class="group-box-wrapper">
|
||||
<div class="group-box">
|
||||
<h3>{name}</h3>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
|
@ -1,20 +1,11 @@
|
|||
<style>
|
||||
div {
|
||||
color: black;
|
||||
background-color: lightcoral;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
a {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
---
|
||||
import GroupBox from "@components/GroupBox.astro";
|
||||
---
|
||||
|
||||
<noscript>
|
||||
<div>
|
||||
<GroupBox>
|
||||
<p>
|
||||
<h4>ENABLE JAVASCRIPT OR I WILL KILL YOU</h4>
|
||||
<h2>ENABLE JAVASCRIPT OR I WILL KILL YOU</h2>
|
||||
I will not. However, you seem to be living in fear of technology, as
|
||||
you have JavaScript disabled. This means that some features (eg. dragging,
|
||||
submitting forms) will not work. Enable JS to fully enjoy this website!
|
||||
|
@ -24,5 +15,5 @@
|
|||
>nin0/website</a
|
||||
> repository on my Forgejo.
|
||||
</p>
|
||||
</div>
|
||||
</GroupBox>
|
||||
</noscript>
|
|
@ -1,16 +1,16 @@
|
|||
---
|
||||
import Chip from "./Chip.astro";
|
||||
import Chip from "@components/Chip.astro";
|
||||
import Envelope from "@assets/svg/envelope.svg";
|
||||
import Plane from "@assets/svg/plane.svg";
|
||||
---
|
||||
|
||||
<div>
|
||||
<Chip href="mailto:support@nin0.dev">
|
||||
<Envelope slot="icon" />
|
||||
<Envelope />
|
||||
support@nin0.dev
|
||||
</Chip>
|
||||
<Chip href="https://nin0dev.t.me">
|
||||
<Plane slot="icon" />
|
||||
<Plane />
|
||||
@nin0dev
|
||||
</Chip>
|
||||
</div>
|
26
src/components/windows/main/InfoStack.astro
Normal file
26
src/components/windows/main/InfoStack.astro
Normal file
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
import Avatar from "components/Avatar.astro"
|
||||
import SpotifyCard from "./SpotifyCard.astro"
|
||||
---
|
||||
|
||||
<>
|
||||
<div class="avatar-stack">
|
||||
<Avatar />
|
||||
<div class="text-stack">
|
||||
<h2>nin0</h2>
|
||||
<sub>he/him</sub>
|
||||
</div>
|
||||
</div>
|
||||
<SpotifyCard />
|
||||
</>
|
||||
<style>
|
||||
.avatar-stack {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.text-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
|
@ -1,29 +1,42 @@
|
|||
---
|
||||
import Contacts from "./Contacts.astro";
|
||||
import GroupBox from "@components/GroupBox.astro";
|
||||
import Me from "./Me";
|
||||
import InfoStack from "./InfoStack.astro";
|
||||
import Window from "@components/Window.astro";
|
||||
import Button from "@components/Button.astro";
|
||||
import GroupBox from "@components/GroupBox.astro";
|
||||
---
|
||||
|
||||
<script>
|
||||
import { WindowManager } from "@models/WindowManager";
|
||||
import { Lanyard } from "@models/Lanyard";
|
||||
import { LANYARD_ID } from "utils/constants";
|
||||
|
||||
document
|
||||
.querySelector("#show-code-window")
|
||||
.addEventListener("click", () => WindowManager.topmost = document.getElementById("window-code-window"));
|
||||
|
||||
new Lanyard(LANYARD_ID, presence =>
|
||||
window.dispatchEvent(new CustomEvent('lanyard-update', {
|
||||
detail: presence
|
||||
}))
|
||||
);
|
||||
</script>
|
||||
|
||||
<Window title="Home" maxWidth="600px" showClose={false}>
|
||||
<Me client:only />
|
||||
<InfoStack />
|
||||
|
||||
<Button position="left" id="show-code-window">My code</Button>
|
||||
<GroupBox name="About me">
|
||||
|
||||
<GroupBox>
|
||||
<h3>About me</h3>
|
||||
<p>
|
||||
I'm a Canadian self-taught software developer.
|
||||
I also make things that some people use with varying degrees of usefulness.
|
||||
</p>
|
||||
</GroupBox>
|
||||
<GroupBox name="Reach out">
|
||||
|
||||
<GroupBox>
|
||||
<h3>Reach out</h3>
|
||||
<Contacts />
|
||||
</GroupBox>
|
||||
</Window>
|
184
src/components/windows/main/SpotifyCard.astro
Normal file
184
src/components/windows/main/SpotifyCard.astro
Normal file
|
@ -0,0 +1,184 @@
|
|||
---
|
||||
import LastFMIcon from "@assets/svg/fm.svg"
|
||||
---
|
||||
<div class="spotify-card-wrapper-shadow">
|
||||
<div class="spotify-card-wrapper">
|
||||
<div class="spotify-card">
|
||||
<svg aria-hidden="true" class="blur">
|
||||
<filter id='sharpBlur' color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation='100'></feGaussianBlur>
|
||||
<feColorMatrix type='matrix' values='1 0 0 0 0, 0 1 0 0 0, 0 0 1 0 0, 0 0 0 9 0'></feColorMatrix>
|
||||
<feComposite in2='SourceGraphic' operator='in'></feComposite>
|
||||
</filter>
|
||||
</svg>
|
||||
<div class="spotify-card-background"></div>
|
||||
|
||||
<div class="spotify-card-art-shadow">
|
||||
<div class="spotify-card-art"></div>
|
||||
</div>
|
||||
<div class="spotfiy-card-track-info">
|
||||
<div class="spotify-card-track-name"></div>
|
||||
<div class="spotify-card-track-artist"></div>
|
||||
<div class="spotify-card-track-album"></div>
|
||||
</div>
|
||||
|
||||
<div class="links">
|
||||
<a class="lastfm-link" href="https://www.last.fm/user/nin0dev"><LastFMIcon /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.blur {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.spotify-card-wrapper-shadow {
|
||||
display: none;
|
||||
filter: drop-shadow(0 0 5px #00000040);
|
||||
}
|
||||
|
||||
.spotify-card-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
background: var(--album-color);
|
||||
clip-path: var(--clip-sp-1);
|
||||
}
|
||||
|
||||
.spotify-card {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 12px;
|
||||
padding: 15px;
|
||||
align-items: center;
|
||||
clip-path: var(--clip-sp-2);
|
||||
}
|
||||
|
||||
.spotify-card-background {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 105%;
|
||||
height: 105%;
|
||||
background: var(--album-art);
|
||||
background-size: 100% 100%;
|
||||
filter: url("#sharpBlur") brightness(0.75);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.spotify-card-art-shadow {
|
||||
filter: drop-shadow(0 0 5px #00000040);
|
||||
}
|
||||
|
||||
.spotify-card-art-wrapper {
|
||||
clip-path: var(--clip-sp-art-wrapper);
|
||||
background: var(--album-color);
|
||||
}
|
||||
.spotify-card-art {
|
||||
background-image: var(--album-art);
|
||||
background-size: 100%;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
clip-path: var(--clip-sp-art);
|
||||
}
|
||||
|
||||
.spotfiy-card-track-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.spotify-card-track-name {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.spotify-card-track-name::after {
|
||||
content: var(--track);
|
||||
font-weight: 500;
|
||||
font-size: 1.05rem;
|
||||
color: #ffffffd5;
|
||||
filter: drop-shadow(0 0 5px #0000006d);
|
||||
}
|
||||
|
||||
.spotify-card-track-artist::after, .spotify-card-track-album::after {
|
||||
color: #ffffffa1;
|
||||
font-weight: 500;
|
||||
font-size: 0.85rem;
|
||||
filter: drop-shadow(0 0 5px #00000040);
|
||||
}
|
||||
|
||||
.spotify-card-track-artist::after {
|
||||
content: var(--artist);
|
||||
}
|
||||
|
||||
.spotify-card-track-album::after {
|
||||
content: var(--album);
|
||||
}
|
||||
|
||||
.links {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.links a {
|
||||
color: rgba(255, 255, 255, 0.516);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { mergeStyles } from "utils/mergeStyles";
|
||||
import { average } from "color.js";
|
||||
import { LASTFM_KEY, LASTFM_API } from "utils/constants";
|
||||
const spotifyCardWrapper: HTMLElement = document.querySelector(".spotify-card-wrapper-shadow");
|
||||
|
||||
async function getLastFmTracks() {
|
||||
const params = new URLSearchParams({
|
||||
method: "user.getrecenttracks",
|
||||
api_key: LASTFM_KEY,
|
||||
user: "nin0dev",
|
||||
limit: "50",
|
||||
format: "json",
|
||||
extended: "true"
|
||||
});
|
||||
|
||||
const response = await(
|
||||
await fetch(`${LASTFM_API}${params}`)
|
||||
).json();
|
||||
|
||||
// TODO: make lastfm stuff typed
|
||||
if (
|
||||
response.recenttracks.track.some(t => {
|
||||
try {
|
||||
return t["@attr"].nowplaying === "true";
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
) {
|
||||
const track = response.recenttracks.track[0];
|
||||
|
||||
mergeStyles({
|
||||
"display": "block",
|
||||
"--album-art": `url("${track.image[1]["#text"]}")`,
|
||||
"--track": `'${track.name}'`,
|
||||
"--album": `'${track.album["#text"]}'`,
|
||||
"--album-color": await average(
|
||||
`${track.image[1]["#text"]}`,
|
||||
{
|
||||
format: "hex"
|
||||
}
|
||||
),
|
||||
"--artist": `'${track.artist.name}'`
|
||||
}, spotifyCardWrapper.style);
|
||||
|
||||
(document.querySelector(".lastfm-link") as HTMLLinkElement).href = track.url;
|
||||
}
|
||||
}
|
||||
|
||||
getLastFmTracks();
|
||||
setInterval(getLastFmTracks, 10000);
|
||||
</script>
|
|
@ -1,245 +0,0 @@
|
|||
import "@css/me.css";
|
||||
import type { LanyardPresence } from "types/lanyard";
|
||||
import { Lanyard } from "models/Lanyard";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { average } from "color.js";
|
||||
|
||||
const lightenColor = (color: string, percent: number) =>
|
||||
`hsl(from ${color} h s ${percent.toString()}%)`;
|
||||
const apiKey = "3c5623fa1abbd11c49f53ca18e992ead";
|
||||
|
||||
export default function Me() {
|
||||
// me-e-eeeee
|
||||
const [presence, setPresence] = useState<LanyardPresence | undefined>();
|
||||
const [currentlyPlaying, setCurrentlyPlaying] = useState<
|
||||
| {
|
||||
title: string;
|
||||
artist: string;
|
||||
album: string;
|
||||
albumArt: string;
|
||||
mainColor: string;
|
||||
loved: boolean;
|
||||
streak: number;
|
||||
spotifyURL?: string;
|
||||
}
|
||||
| undefined
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
new Lanyard("886685857560539176", p => setPresence(p));
|
||||
|
||||
async function getLastFmTracks() {
|
||||
const params = new URLSearchParams({
|
||||
method: "user.getrecenttracks",
|
||||
api_key: apiKey,
|
||||
user: "nin0dev",
|
||||
limit: "50",
|
||||
format: "json",
|
||||
extended: "true"
|
||||
});
|
||||
|
||||
const res = await (
|
||||
await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`)
|
||||
).json();
|
||||
|
||||
if (
|
||||
res.recenttracks.track.some(t => {
|
||||
try {
|
||||
return t["@attr"].nowplaying === "true";
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
) {
|
||||
const currentlyPlayingTrack = res.recenttracks.track[0];
|
||||
|
||||
const trackExtra = (
|
||||
await (
|
||||
await fetch(
|
||||
`https://ws.audioscrobbler.com/2.0/?${new URLSearchParams(
|
||||
{
|
||||
method: "track.getinfo",
|
||||
api_key: apiKey,
|
||||
format: "json",
|
||||
track: currentlyPlayingTrack.name,
|
||||
artist: currentlyPlayingTrack.artist.name
|
||||
}
|
||||
)}`
|
||||
)
|
||||
).json()
|
||||
).track;
|
||||
|
||||
setCurrentlyPlaying({
|
||||
title: currentlyPlayingTrack.name,
|
||||
artist: currentlyPlayingTrack.artist.name,
|
||||
album: currentlyPlayingTrack.album["#text"],
|
||||
albumArt: currentlyPlayingTrack.image[1]["#text"],
|
||||
// @ts-ignore this library is poorly typed
|
||||
mainColor: await average(
|
||||
currentlyPlayingTrack.image[1]["#text"],
|
||||
{
|
||||
format: "hex"
|
||||
}
|
||||
),
|
||||
loved: currentlyPlayingTrack.loved === "1",
|
||||
streak: (() => {
|
||||
let current = 0;
|
||||
for (const track of res.recenttracks.track) {
|
||||
if (track.name === currentlyPlayingTrack.name)
|
||||
current++;
|
||||
else return current;
|
||||
}
|
||||
})()
|
||||
});
|
||||
} else {
|
||||
setCurrentlyPlaying(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
getLastFmTracks();
|
||||
setInterval(getLastFmTracks, 10000);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="me-container">
|
||||
<div
|
||||
className="img-container"
|
||||
style={{
|
||||
backgroundColor: presence
|
||||
? ["online", "idle"].includes(
|
||||
presence.discord_status
|
||||
)
|
||||
? "var(--green)"
|
||||
: "var(--overlay0)"
|
||||
: "var(--overlay0)"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="logo.png"
|
||||
alt="the nin0 logo"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h2>
|
||||
nin0
|
||||
</h2>
|
||||
<h4>
|
||||
he/him
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{currentlyPlaying && (
|
||||
<div
|
||||
className="spotify-card-wrapper"
|
||||
style={{
|
||||
"--album-color": currentlyPlaying.mainColor,
|
||||
"--album-art": `url(${currentlyPlaying.albumArt})`
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<div
|
||||
className="spotify-card"
|
||||
>
|
||||
<h3
|
||||
style={{
|
||||
color: lightenColor(currentlyPlaying.mainColor, 90)
|
||||
}}
|
||||
>
|
||||
Listening to
|
||||
</h3>
|
||||
<div
|
||||
className="spotify-card-content"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: "10px"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={currentlyPlaying.albumArt}
|
||||
style={{
|
||||
marginRight: "10px",
|
||||
width: "50px",
|
||||
borderRadius: "5px"
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<p
|
||||
style={{
|
||||
margin: "0",
|
||||
fontWeight: "600",
|
||||
color: lightenColor(
|
||||
currentlyPlaying.mainColor,
|
||||
90
|
||||
)
|
||||
}}
|
||||
>
|
||||
{currentlyPlaying.title}
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
margin: "0",
|
||||
color: lightenColor(
|
||||
currentlyPlaying.mainColor,
|
||||
85
|
||||
)
|
||||
}}
|
||||
className="secondary-meta"
|
||||
>
|
||||
{currentlyPlaying.artist}
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
margin: "0",
|
||||
color: lightenColor(
|
||||
currentlyPlaying.mainColor,
|
||||
85
|
||||
)
|
||||
}}
|
||||
className="secondary-meta"
|
||||
>
|
||||
{currentlyPlaying.album}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ fontSize: "0.9rem" }} className="emoji">
|
||||
{(() => {
|
||||
const flags: {
|
||||
color: string;
|
||||
data: string;
|
||||
}[] = [];
|
||||
if (currentlyPlaying.loved)
|
||||
flags.push({
|
||||
color: "#f38ba8",
|
||||
data: "💞 Loved track"
|
||||
});
|
||||
if (currentlyPlaying.streak > 1) {
|
||||
flags.push({
|
||||
color: "#f9e2af",
|
||||
data: `⚡ Played ${currentlyPlaying.streak} times in a row`
|
||||
});
|
||||
}
|
||||
return flags.map(flag => (
|
||||
<span
|
||||
style={{ color: flag.color }}
|
||||
className="emoji"
|
||||
key={flag.color}
|
||||
>
|
||||
{flag.data}{" "}
|
||||
{flags.indexOf(flag) !==
|
||||
flags.length - 1 && (
|
||||
<span style={{ color: "white" }}>
|
||||
{" "}•{" "}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
));
|
||||
})()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue