first commit
This commit is contained in:
commit
b5a4c0a601
42 changed files with 2802 additions and 0 deletions
417
src/App.css
Normal file
417
src/App.css
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
:root {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* browsers agreeing on stuff challenge (impossible) */
|
||||
background-color: #121212;
|
||||
font-family: monospace;
|
||||
overflow-x: hidden;
|
||||
color: #ffc8dd;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #cdb4db;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: 10px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.musicartist {
|
||||
font-weight: bolder;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.musicbutton {
|
||||
background-color: #272525;
|
||||
color: pink;
|
||||
font-family: Roboto, sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
padding: 10px 30px;
|
||||
box-shadow: none;
|
||||
border-radius: 5px;
|
||||
transition: 639ms;
|
||||
transform: translateY(0);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.musicdiv {
|
||||
background-color: rgba(0, 0, 0, 0.731);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1500;
|
||||
}
|
||||
|
||||
.music-close-button-div {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
padding: 2;
|
||||
border: none;
|
||||
background: none;
|
||||
border-radius: 2px;
|
||||
background-color: #5e5a5a;
|
||||
text-decoration: none;
|
||||
color: pink;
|
||||
cursor: pointer;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.musiclist {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
font-size: larger;
|
||||
flex-wrap: wrap;
|
||||
height: 80%;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.innermusic {
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
border-radius: 2%;
|
||||
background: #121212;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.musicbutton:hover {
|
||||
border-radius: 5px;
|
||||
transition: 639ms;
|
||||
padding: 10px 30px;
|
||||
transform: translateY(-1px);
|
||||
border: none;
|
||||
box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
-webkit-box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
-moz-box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
background-color: #5e5a5a;
|
||||
animation: big 500ms infinite;
|
||||
transform: translateY(-0.3em);
|
||||
transition:
|
||||
background-color 1s,
|
||||
transform 0.5s,
|
||||
box-shadow 0.5s,
|
||||
-webkit-box-shadow 0.5s,
|
||||
-moz-box-shadow 0.5s;
|
||||
}
|
||||
|
||||
.actualreviewdiv {
|
||||
overflow-y: scroll;
|
||||
height: 18em;
|
||||
}
|
||||
|
||||
.cardchild:hover + .actualreviewdiv {
|
||||
transition: 1s;
|
||||
transform: rotateY(100);
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-top: 6em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cardchild h1:not(:has(.listeningto)) {
|
||||
font-size: 3em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reviewheadertext {
|
||||
margin-bottom: -0.2em;
|
||||
}
|
||||
|
||||
.cardchild p {
|
||||
margin-bottom: -1.5em;
|
||||
}
|
||||
|
||||
.cardchild a {
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: pink;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 0%;
|
||||
.fadein {
|
||||
animation: spawntop 1s;
|
||||
}
|
||||
}
|
||||
|
||||
.cardchild span {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.cardchild img {
|
||||
max-width: 5em;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.cardchild:hover::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* CAN BROWSERS JUST AGREE ON ONE FUCKING THING */
|
||||
|
||||
.cardchild {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
cursor: default;
|
||||
background-color: #272525;
|
||||
border-radius: 5%;
|
||||
width: 25em;
|
||||
height: 25em;
|
||||
margin-top: 1em;
|
||||
padding: 0.5em 5em 2em 5em;
|
||||
transform: translateY(-0em);
|
||||
transition:
|
||||
background-color 1s,
|
||||
transform 0.5s,
|
||||
box-shadow 0.5s,
|
||||
-webkit-box-shadow 0.5s,
|
||||
-moz-box-shadow 0.5s;
|
||||
}
|
||||
|
||||
.theeaster {
|
||||
color: pink;
|
||||
padding: 8px;
|
||||
font-size: 21px;
|
||||
text-align: center;
|
||||
border-width: 0;
|
||||
background-color: #5555557a;
|
||||
border-style: solid;
|
||||
border-radius: 11px;
|
||||
}
|
||||
|
||||
.theeaster:focus {
|
||||
border-color: pink;
|
||||
box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
-webkit-box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
-moz-box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.easteregg {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.easteregginner {
|
||||
width: 70%;
|
||||
background-color: #272525;
|
||||
height: 10em;
|
||||
}
|
||||
|
||||
.cardchild:hover {
|
||||
box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
-webkit-box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
-moz-box-shadow: 0 0 534px -3px rgba(250, 236, 236, 0.4);
|
||||
background-color: #5e5a5a;
|
||||
animation: big 500ms infinite;
|
||||
transform: translateY(-0.3em);
|
||||
transition:
|
||||
background-color 1s,
|
||||
transform 0.5s,
|
||||
box-shadow 0.5s,
|
||||
-webkit-box-shadow 0.5s,
|
||||
-moz-box-shadow 0.5s;
|
||||
}
|
||||
|
||||
.linktree {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.linktree img {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.singlereview img {
|
||||
border-radius: 12%;
|
||||
}
|
||||
|
||||
.singlereview {
|
||||
margin: 1em;
|
||||
border-radius: 3%;
|
||||
background-color: #00000040;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.cssishard {
|
||||
background-color: #121212;
|
||||
}
|
||||
|
||||
.singlemusic {
|
||||
cursor: pointer;
|
||||
min-width: 15em;
|
||||
max-width: 15em;
|
||||
word-wrap: anywhere;
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
border-radius: 3%;
|
||||
background-color: #272525;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.singlemusic img {
|
||||
max-width: 5em;
|
||||
}
|
||||
|
||||
.innermusic {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.reviewname {
|
||||
margin-bottom: 0.1em;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.reviewinfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.reviewname {
|
||||
margin-bottom: 0.1em;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.parent {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 3.6em;
|
||||
color: blanchedalmond;
|
||||
display: inline-table;
|
||||
}
|
||||
|
||||
.cardchild h2 {
|
||||
display: inline-table;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.header h1:not(:active) {
|
||||
animation: slide-right 1.5s;
|
||||
}
|
||||
|
||||
.header h1:hover {
|
||||
animation: shake 10ms infinite;
|
||||
}
|
||||
|
||||
.header img {
|
||||
border-radius: 25%;
|
||||
margin-right: 1em;
|
||||
vertical-align: middle;
|
||||
max-width: 10em;
|
||||
}
|
||||
|
||||
.initialanim {
|
||||
animation: slide-left 1.5s;
|
||||
}
|
||||
|
||||
.gaybackground {
|
||||
background-image: url("/gaybackground.png");
|
||||
}
|
||||
|
||||
.animate {
|
||||
z-index: 10000000;
|
||||
transform: scale(10000);
|
||||
transition: 20s;
|
||||
}
|
||||
|
||||
@keyframes slide-left {
|
||||
from {
|
||||
transform: translateX(-10000em);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-right {
|
||||
from {
|
||||
transform: translateX(10000em);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bigsmall {
|
||||
0% {
|
||||
max-width: 10em;
|
||||
}
|
||||
|
||||
50% {
|
||||
width: 10.5em;
|
||||
max-width: 10.5em;
|
||||
}
|
||||
|
||||
100% {
|
||||
max-width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spawntop {
|
||||
0% {
|
||||
opacity: 0%;
|
||||
transform: translateY(1em);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 100%;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0% {
|
||||
transform: translate(0, 0) rotate(0deg);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translate(5px, 5px) rotate(5deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(0, 0) rotate(0eg);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translate(-5px, 5px) rotate(-5deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 0) rotate(0deg);
|
||||
}
|
||||
}
|
||||
255
src/App.tsx
Normal file
255
src/App.tsx
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
import './App.css'
|
||||
import {AdvancedBr, Singular88, SingularOomfie} from './components/comps.tsx'
|
||||
import {createSignal, onMount} from 'solid-js';
|
||||
import Reviews from './components/api.tsx';
|
||||
import Music, {MusicEntry} from './components/music.tsx';
|
||||
import {Bdpfp, Normalpfp} from './components/pfp.tsx';
|
||||
import {InfoCard} from './components/middlecard.tsx';
|
||||
|
||||
export const [shouldpopup, setpopup] = createSignal(false)
|
||||
export const [shouldpopup88, setpopup88] = createSignal(false)
|
||||
export const [shouldpopupEasterEgg, setpopupEasterEgg] = createSignal(false)
|
||||
export const [ishover, setishover] = createSignal(false)
|
||||
|
||||
let explodcount = 0
|
||||
const isitmybd = () => new Date().toISOString().slice(5, 10) === '08-22';
|
||||
|
||||
function getRandomVivsieWord() {
|
||||
const words = [
|
||||
"fuck",
|
||||
"shit",
|
||||
"pussy",
|
||||
"penis",
|
||||
"dick"
|
||||
]
|
||||
return words[Math.floor(Math.random() * words.length)]
|
||||
}
|
||||
|
||||
function vivsiepop() {
|
||||
const blep = document.body.childNodes;
|
||||
|
||||
function fuckshit(node: ChildNode) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
node.textContent = getRandomVivsieWord();
|
||||
} else {
|
||||
node.childNodes.forEach(fuckshit);
|
||||
}
|
||||
}
|
||||
|
||||
blep.forEach(fuckshit)
|
||||
}
|
||||
|
||||
function nyaboom() {
|
||||
explodcount++
|
||||
const blep = document.body.childNodes;
|
||||
|
||||
function fuckshit(node: ChildNode) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
(node as Element).textContent = ''
|
||||
node.parentElement?.appendChild(<img style={{
|
||||
width: "1.5em"
|
||||
}} src="./explod.gif"/> as Element)
|
||||
} else if (node instanceof HTMLImageElement) {
|
||||
node.src = "./explod.gif"
|
||||
} else {
|
||||
node.childNodes.forEach(fuckshit);
|
||||
}
|
||||
}
|
||||
|
||||
blep.forEach(fuckshit)
|
||||
}
|
||||
|
||||
function App() {
|
||||
|
||||
const [musicList, setMusicList] = createSignal<string[]>([]);
|
||||
const [isLoading, setIsLoading] = createSignal(true);
|
||||
const [oomfies, setoomfies] = createSignal(<>oomfies</>)
|
||||
const [isAnimating, setIsAnimating] = createSignal(false);
|
||||
onMount(async () => {
|
||||
try {
|
||||
const response = await fetch("https://imtoolazytomakeaproperapi.exhq.dev/");
|
||||
const data = await response.json();
|
||||
setMusicList(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching music data:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
});
|
||||
let gitgay = <img onClick={() => {
|
||||
setIsAnimating(true)
|
||||
setTimeout(() => {
|
||||
window.location.href = "https://git.lgbt/exhq"
|
||||
}, 200);
|
||||
gitgay.src = "/gaybackground.png"
|
||||
}} classList={{'gitgayimg': true, 'animate': isAnimating(), 'gaybackground': isAnimating()}}
|
||||
src="https://proxy.spiro.exhq.dev/_/plain/https://git.lgbt/assets/img/logo.png"
|
||||
alt="logo of git.lgbt"/> as HTMLImageElement
|
||||
// @ts-ignore
|
||||
return (
|
||||
<>
|
||||
{isitmybd() ? <Bdpfp setpopupEasterEgg={setpopupEasterEgg}/> :
|
||||
<Normalpfp setpopupEasterEgg={setpopupEasterEgg}/>}
|
||||
|
||||
<p style={{display: "none"}}>AdvancedBr my beloved</p>
|
||||
<AdvancedBr count={6}/>
|
||||
|
||||
|
||||
<div class='parent'>
|
||||
<div class='cardchild'>
|
||||
<h1>link tree</h1>
|
||||
<div class='linktree'>
|
||||
{gitgay}
|
||||
|
||||
</div>
|
||||
<br/>
|
||||
<div class='linktree'>
|
||||
<span class='gitgaytext'>Git</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
opacity: isAnimating() ? "0%" : "100%"
|
||||
}} class='cardchild'>
|
||||
<InfoCard bd={isitmybd()}/>
|
||||
|
||||
</div>
|
||||
<div
|
||||
onMouseEnter={() => {
|
||||
setishover(true)
|
||||
}}
|
||||
onmouseleave={() => {
|
||||
setishover(false)
|
||||
}}
|
||||
style={{
|
||||
opacity: isAnimating() ? "0%" : "100%"
|
||||
}} class='cardchild'>
|
||||
<Reviews/>
|
||||
</div>
|
||||
</div>
|
||||
<AdvancedBr count={2}/>
|
||||
<div class='easteregg' style={{opacity: isAnimating() ? "0%" : "100%"}}>
|
||||
<div class="musicbutton" onClick={() => {
|
||||
setpopup(!shouldpopup())
|
||||
}}>
|
||||
<p>typa shit ive been on</p><img style={{
|
||||
"margin-left": "0.3em",
|
||||
"max-width": "1.5em"
|
||||
}} src="./fireemoji.png"/>
|
||||
</div>
|
||||
<div class="musicbutton">
|
||||
<div class="oomfies" onClick={() => {
|
||||
setoomfies(
|
||||
<>
|
||||
<SingularOomfie name='ashley' discordid='836177139798638592'
|
||||
url='https://ashleygraves.eu/'></SingularOomfie>
|
||||
<SingularOomfie name='nea' discordid='310702108997320705'
|
||||
url='https://nea.moe'></SingularOomfie>
|
||||
<SingularOomfie name='vozy' discordid='359175647257690113'
|
||||
url='https://vozy.exhq.dev'></SingularOomfie>
|
||||
<SingularOomfie name='hazel' discordid='435026627907420161'
|
||||
url='https://yellows.ink/'></SingularOomfie>
|
||||
<SingularOomfie name='nax' discordid='148801388938264576'
|
||||
url='https://nax.dev/'></SingularOomfie>
|
||||
<SingularOomfie name='squirrelly' discordid='218032723296649217'
|
||||
url='https://squirrelly13.neocities.org/'></SingularOomfie>
|
||||
<SingularOomfie name='ushie' discordid='399862294143696897'
|
||||
url='https://ushie.dev/'></SingularOomfie>
|
||||
<SingularOomfie name='mugman' discordid='601836455006044163'
|
||||
url='https://mugman.tech'></SingularOomfie>
|
||||
<SingularOomfie name='krystal' discordid='929208515883569182'
|
||||
url='https://krystal.exhq.dev/'></SingularOomfie>
|
||||
|
||||
</>
|
||||
)
|
||||
}}> {oomfies()}</div>
|
||||
</div>
|
||||
<div class="musicbutton" onClick={vivsiepop}>
|
||||
<p>echo if it was written by vivsiepop</p>
|
||||
</div>
|
||||
<div class="musicbutton" onClick={() => {
|
||||
setpopup88(true)
|
||||
}}>
|
||||
<p>88x31's</p>
|
||||
</div>
|
||||
<div class="musicbutton" onClick={() => {
|
||||
if (explodcount > 5) {
|
||||
document.body.innerHTML = ""
|
||||
setTimeout(() => {
|
||||
alert("sorry bud, you exploded so much that my document.body is gon")
|
||||
}, 500)
|
||||
} else {
|
||||
new Audio("./explod.mp3").play()
|
||||
nyaboom()
|
||||
}
|
||||
}}>
|
||||
<img style={{
|
||||
"margin-left": "0.3em",
|
||||
"max-width": "1.5em"
|
||||
}} src="./nyaboom.webp"/>
|
||||
</div>
|
||||
</div>
|
||||
<AdvancedBr count={3}/>
|
||||
<Music shouldpopup={shouldpopup}>
|
||||
<div class='musicdiv'>
|
||||
<div class='innermusic'>
|
||||
<div class="music-close-button-div">
|
||||
<button class="close-button" onClick={() => {
|
||||
setpopup(false)
|
||||
}}>X
|
||||
</button>
|
||||
</div>
|
||||
<div class='musiclist'>
|
||||
{isLoading() ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
musicList().map((link) => <MusicEntry spotifylink={link}/>)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Music>
|
||||
<Music shouldpopup={shouldpopupEasterEgg}>
|
||||
<div class='musicdiv'>
|
||||
<div class='innermusic'>
|
||||
<div class="music-close-button-div">
|
||||
<button class="close-button" onClick={() => {
|
||||
setpopupEasterEgg(false)
|
||||
}}>X
|
||||
</button>
|
||||
</div>
|
||||
<img src={"https://pico.exhq.dev/-Aax47Gmdsy"}/>
|
||||
</div>
|
||||
</div>
|
||||
</Music>
|
||||
<Music shouldpopup={shouldpopup88}>
|
||||
<div class='musicdiv'>
|
||||
<div class='innermusic'>
|
||||
<div class="music-close-button-div">
|
||||
<button class="close-button" onClick={() => {
|
||||
setpopup88(false)
|
||||
}}>X
|
||||
</button>
|
||||
</div>
|
||||
<div style={{}}>
|
||||
<img src="https://exhq.dev/88x31.png" alt=""/>
|
||||
<br/>
|
||||
<span>feel free to link mine, <code>https://exhq.dev/88x31.png</code></span>
|
||||
<div style={{"background-color": "gray", height: "1px"}}/>
|
||||
<div>
|
||||
<Singular88 name="nax" url="https://nax.dev" src="https://nax.dev/88x31.gif"/>
|
||||
<Singular88 name="sophari" url="https://sophari.org" src="https://sophari.org/img/sophari.gif"/>
|
||||
<Singular88 name="rini" url="https://rinici.de/" src="https://rinici.de/button.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Music>
|
||||
<div class='footer'> <a href="https://ko-fi.com/amyarson">support me ♥</a><br/>made with ♥ by amy. <a
|
||||
href="https://git.lgbt/exhq/v2.exhq.dev">this website is
|
||||
opensource</a></div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
115
src/components/api.tsx
Normal file
115
src/components/api.tsx
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import {createSignal, onMount} from "solid-js"
|
||||
import {ishover} from "../App";
|
||||
|
||||
interface Review {
|
||||
reviewID: number;
|
||||
discordID: string;
|
||||
reviewText: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
interface NeoReview extends Review {
|
||||
global_name: string;
|
||||
username: string
|
||||
}
|
||||
|
||||
|
||||
export default function Reviews() {
|
||||
const [reviews, setReviews] = createSignal<NeoReview[]>([]);
|
||||
onMount(() => {
|
||||
fetch("https://review.exhq.dev/getreviews")
|
||||
.then(response => response.json())
|
||||
.then((data: Review[]) => {
|
||||
const promises = data.map(review => (
|
||||
fetch(`https://dc-lookup.spiro.exhq.dev/v1/user/${review.discordID}`)
|
||||
.then(response => response.json())
|
||||
.then(user => ({
|
||||
...review,
|
||||
global_name: user.global_name,
|
||||
username: user.username
|
||||
}))
|
||||
));
|
||||
|
||||
Promise.all(promises)
|
||||
.then(yeah => {
|
||||
setReviews(yeah);
|
||||
})
|
||||
.catch(error => console.error("Error fetching Discord user data:", error));
|
||||
})
|
||||
.catch(error => console.error("Error fetching reviews:", error));
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<a style={{
|
||||
display: ishover() ? "inline" : "none"
|
||||
}}
|
||||
href="https://discord.com/oauth2/authorize?client_id=1208380910525743134&response_type=token&redirect_uri=https%3A%2F%2Fexhq.dev%2Freview%2F&scope=identify">
|
||||
<p class="fadein">add your reviews here</p>
|
||||
</a>
|
||||
<h1 class="reviewheadertext">Reviews</h1>
|
||||
<div
|
||||
class="actualreviewdiv">
|
||||
{reviews().length > 0 ? (
|
||||
reviews().reverse().map((review) => (
|
||||
<div>
|
||||
<SingleReview {...review} />
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div>Loading reviews...</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export const theImager = async (id: string): Promise<string> => (await fetch(`https://dc-lookup.spiro.exhq.dev/v1/user/${id}`)
|
||||
.then(res => res.json()).then(data => "https://proxy.spiro.exhq.dev/_/plain/"+data.avatar.link).catch(() => "https://http.cat/status/100"));
|
||||
|
||||
function SingleReview(props: NeoReview) {
|
||||
const [imageSrc, setImageSrc] = createSignal("");
|
||||
|
||||
onMount(async () => {
|
||||
const url = await theImager(props.discordID);
|
||||
setImageSrc(url);
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="singlereview">
|
||||
<img
|
||||
src={imageSrc()}
|
||||
alt="User Avatar"
|
||||
style={{"max-width": "3em", "border-radius": "30%"}}
|
||||
/>
|
||||
<div class="reviewinfo">
|
||||
<div class="reviewname">
|
||||
{props.global_name === null ? props.username : props.global_name}
|
||||
</div>
|
||||
<div class="reviewtext">{props.reviewText}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function sendReview(review: string, token: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(`https://review.exhq.dev/sendreview?review=${review}`, {
|
||||
headers: {
|
||||
"Auth": token,
|
||||
},
|
||||
method: "POST"
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
9
src/components/comps.css
Normal file
9
src/components/comps.css
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
.tooltip:hover {
|
||||
max-width: 4em !important;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
animation: ease-in-out 2s infinite;
|
||||
padding: 1em;
|
||||
}
|
||||
67
src/components/comps.tsx
Normal file
67
src/components/comps.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { createSignal, onMount } from "solid-js";
|
||||
import { theImager } from "./api"
|
||||
import "./comps.css"
|
||||
// warning: this IS horrible code. its a joke. DO NOT try this at home because
|
||||
// your local senior programmer CAN and WILL hunt you down
|
||||
// you have been warned.
|
||||
export function AdvancedBr({ count }: { count: number }) {
|
||||
return new Array(count).fill(null).map(_ => (<br />))
|
||||
}
|
||||
|
||||
export function SingularOomfie(props: { name: string; url: string; discordid: string }) {
|
||||
const [imageSrc, setImageSrc] = createSignal("");
|
||||
const [showTooltip, setShowTooltip] = createSignal(false);
|
||||
|
||||
onMount(async () => {
|
||||
const url = await theImager(props.discordid);
|
||||
setImageSrc(url);
|
||||
});
|
||||
|
||||
const handleMouseMove = () => {
|
||||
setShowTooltip(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setShowTooltip(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
style={{ position: "relative", display: "inline-block" }}
|
||||
>
|
||||
<a href={props.url}>
|
||||
<img
|
||||
class="tooltip"
|
||||
style={{
|
||||
"max-width": "3em",
|
||||
"border-radius": "30%",
|
||||
}}
|
||||
src={imageSrc()}
|
||||
alt={"profile picture for " + props.name}
|
||||
/>
|
||||
</a>
|
||||
{showTooltip() && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
color: "white",
|
||||
padding: "0.5em",
|
||||
"border-radius": "5px",
|
||||
"pointer-events": "none",
|
||||
transform: "translate(20%, -100%)",
|
||||
}}
|
||||
>
|
||||
{props.name}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function Singular88(props: { name: string; url: string; src:string }) {
|
||||
return <a href={props.url}><img alt={props.name} src={props.src}/></a>
|
||||
}
|
||||
6
src/components/cumbrainz.css
Normal file
6
src/components/cumbrainz.css
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.listeningto {
|
||||
font-size: smaller;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
65
src/components/cumbrainz.tsx
Normal file
65
src/components/cumbrainz.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import {createSignal, onMount, Show} from "solid-js";
|
||||
import "./cumbrainz.css"
|
||||
|
||||
|
||||
interface ListenPayload {
|
||||
payload: {
|
||||
count: number;
|
||||
listens: Listen[];
|
||||
playing_now: boolean;
|
||||
user_id: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Listen {
|
||||
playing_now: boolean;
|
||||
track_metadata: TrackMetadata;
|
||||
}
|
||||
|
||||
interface TrackMetadata {
|
||||
additional_info: AdditionalInfo;
|
||||
artist_name: string;
|
||||
release_name: string;
|
||||
track_name: string;
|
||||
}
|
||||
|
||||
interface AdditionalInfo {
|
||||
duration: number;
|
||||
music_service_name: string;
|
||||
origin_url: string;
|
||||
submission_client: string;
|
||||
submission_client_version: string;
|
||||
}
|
||||
|
||||
export function Cumbrainz() {
|
||||
const [musicInfo, setMusicInfo] = createSignal({} as ListenPayload);
|
||||
const [isLoading, setIsLoading] = createSignal(true);
|
||||
onMount(async () => {
|
||||
try {
|
||||
const the = await fetch("https://music.exhq.dev/")
|
||||
const thejson = await the.json()
|
||||
setMusicInfo(thejson)
|
||||
} catch (error) {
|
||||
console.error("Error fetching music data from song.link:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
return <div class="listeningto">
|
||||
<h2 >Listening to</h2>
|
||||
<Show when={isLoading()} fallback={
|
||||
<Show when={musicInfo().payload.listens.length > 0} fallback={<span>nothing</span>}>
|
||||
<Thesong song={musicInfo()} />
|
||||
</Show>
|
||||
}>
|
||||
<span>loading</span>
|
||||
</Show>
|
||||
</div>
|
||||
}
|
||||
|
||||
function Thesong({song}: { song: ListenPayload }) {
|
||||
return <a
|
||||
href={song.payload.listens[0].track_metadata.additional_info.origin_url.replace(/\?.*/, m => `?v=${new URLSearchParams(m).get('v') ?? ''}`).replace(/(\?v=)$/, '')}>
|
||||
<div> {song.payload.listens[0].track_metadata.artist_name} - {song.payload.listens[0].track_metadata.track_name} </div>
|
||||
</a>
|
||||
}
|
||||
21
src/components/events.tsx
Normal file
21
src/components/events.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { onMount } from 'solid-js';
|
||||
|
||||
const ReloadOnBack = () => {
|
||||
const handlePageShow = (event: Event) => {
|
||||
const pageshowEvent = event as PageTransitionEvent;
|
||||
if (pageshowEvent.persisted) {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener('pageshow', handlePageShow);
|
||||
return () => {
|
||||
window.removeEventListener('pageshow', handlePageShow);
|
||||
};
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ReloadOnBack;
|
||||
11
src/components/middlecard.css
Normal file
11
src/components/middlecard.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.middleparent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.middlechild {
|
||||
text-align: center;
|
||||
}
|
||||
33
src/components/middlecard.tsx
Normal file
33
src/components/middlecard.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { Cumbrainz } from "./cumbrainz"
|
||||
import "./middlecard.css"
|
||||
export interface InfoCardProps {
|
||||
bd: boolean
|
||||
}
|
||||
|
||||
export function InfoCard(props: InfoCardProps) {
|
||||
|
||||
return <div class="middleparent">
|
||||
<div class="middlechild">{
|
||||
props.bd ?
|
||||
<>
|
||||
<span>
|
||||
its my birthday
|
||||
<br />
|
||||
please buy me stuff
|
||||
</span>
|
||||
</> : <>
|
||||
<span>silly goober who does silly stuff
|
||||
<br />
|
||||
self proclaimed programmer and progamer
|
||||
<br />
|
||||
shitposts for fun
|
||||
</span>
|
||||
</>}</div>
|
||||
<div style={{"background-color": "gray", height: "1px"}}>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="middlechild">
|
||||
<Cumbrainz></Cumbrainz>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
47
src/components/music.tsx
Normal file
47
src/components/music.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { createSignal, onMount } from "solid-js";
|
||||
|
||||
export default function Music(props: { shouldpopup: () => boolean, children: any }) {
|
||||
return <div style={{
|
||||
position: "absolute",
|
||||
display: props.shouldpopup() ? "block" : "none"
|
||||
}}>
|
||||
{props.children}
|
||||
</div>
|
||||
}
|
||||
|
||||
export function MusicEntry({ spotifylink }: { spotifylink: string }) {
|
||||
const [musicInfo, setMusicInfo] = createSignal({}) as any;
|
||||
const [isLoading, setIsLoading] = createSignal(true);
|
||||
onMount(async () => {
|
||||
try {
|
||||
const apiresponse = await fetch(`https://corsisdum.exhq.dev/v1-alpha.1/links?url=spotify%253Atrack%253A${spotifylink}`)
|
||||
const data = await apiresponse.json()
|
||||
setMusicInfo(data)
|
||||
} catch (error) {
|
||||
console.error("Error fetching music data from song.link:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
return (
|
||||
<>{isLoading() ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<div onClick={() => {
|
||||
document.location.href = musicInfo().pageUrl
|
||||
}} class="singlemusic">
|
||||
<img style={{
|
||||
"margin-right": "0.5em"
|
||||
}} src={"https://proxy.spiro.exhq.dev/_/plain/" + musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.thumbnailUrl} alt={"album cover of" + musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.title} />
|
||||
<div class="innersinglemusic">
|
||||
<div class="musicartist" > {
|
||||
musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.artistName?.length > 43 ? musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.artistName.substring(0, 30) + "..." : musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.artistName
|
||||
}</div>
|
||||
<div class="musictitle"> {
|
||||
musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.title?.length > 43 ? musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.title.substring(0, 30) + "..." : musicInfo().entitiesByUniqueId[`SPOTIFY_SONG::${spotifylink}`]?.title
|
||||
} </div>
|
||||
</div>
|
||||
</div>
|
||||
)}</>
|
||||
)
|
||||
}
|
||||
31
src/components/name.tsx
Normal file
31
src/components/name.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// import { createSignal, onCleanup } from "solid-js";
|
||||
import { onCleanup } from "solid-js";
|
||||
import { removethething } from "./utils.tsx";
|
||||
|
||||
function HoverComponent() {
|
||||
let timerId: number | null;
|
||||
|
||||
const clearTimer = () => {
|
||||
if (timerId) {
|
||||
clearTimeout(timerId);
|
||||
timerId = null;
|
||||
}
|
||||
};
|
||||
|
||||
onCleanup(() => {
|
||||
clearTimer();
|
||||
});
|
||||
|
||||
return (
|
||||
<h1
|
||||
class="removethisinstantly"
|
||||
onMouseEnter={() => {
|
||||
removethething();
|
||||
}}
|
||||
>
|
||||
Amy
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
export default HoverComponent;
|
||||
40
src/components/pfp.css
Normal file
40
src/components/pfp.css
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
.birthdayparent {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes rotateAnimation {
|
||||
from {
|
||||
transform: rotateY(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotateY(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.rotate {
|
||||
animation: rotateAnimation 0.5s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.header .bd {
|
||||
flex-direction: column;
|
||||
margin-left: 10em;
|
||||
}
|
||||
|
||||
.birthday {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin-left: -12em;
|
||||
}
|
||||
|
||||
.birthdaypfp {
|
||||
z-index: 1;
|
||||
max-width: 10em;
|
||||
}
|
||||
|
||||
.birthdayhat {
|
||||
z-index: 2;
|
||||
width: 14em;
|
||||
top: -3em;
|
||||
left: -1em;
|
||||
}
|
||||
69
src/components/pfp.tsx
Normal file
69
src/components/pfp.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import HoverComponent from "./name";
|
||||
import "./pfp.css";
|
||||
|
||||
export function Bdpfp({
|
||||
setpopupEasterEgg,
|
||||
}: {
|
||||
setpopupEasterEgg: (value: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
setpopupEasterEgg(true);
|
||||
}}
|
||||
class="header bd"
|
||||
>
|
||||
<div class="birthdayparent">
|
||||
<img
|
||||
class="birthdaypfp birthday"
|
||||
src="https://files.amy.rip/pfp.jpg"
|
||||
alt="amy's current discord pfp, with a birthday hat on it"
|
||||
/>
|
||||
<img class="birthdayhat birthday" src="./birthday.png" alt="" />
|
||||
<HoverComponent />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Normalpfp({
|
||||
setpopupEasterEgg,
|
||||
}: {
|
||||
setpopupEasterEgg: (value: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
setpopupEasterEgg(true);
|
||||
}}
|
||||
class="birthdayparent header normal"
|
||||
>
|
||||
<img
|
||||
class="initialanim"
|
||||
src="https://files.amy.rip/pfp.jpg"
|
||||
alt="amy's current discord pfp"
|
||||
onMouseEnter={(e) => {
|
||||
(e.target as HTMLImageElement).animate(
|
||||
[{ transform: "rotateZ(0deg)" }, { transform: "rotateZ(360deg)" }],
|
||||
{
|
||||
duration: 400,
|
||||
iterations: 1,
|
||||
},
|
||||
);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
(e.target as HTMLImageElement).animate(
|
||||
[{ transform: "rotateY(0deg)" }, { transform: "rotateY(360deg)" }],
|
||||
{
|
||||
duration: 150,
|
||||
iterations: 1,
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<HoverComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
35
src/components/utils.tsx
Normal file
35
src/components/utils.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
export function removethething() {
|
||||
for (let i = 0; i < document.styleSheets.length; i++) {
|
||||
let styleSheet = document.styleSheets[i];
|
||||
let rules = styleSheet.cssRules || styleSheet.rules;
|
||||
for (let j = 0; j < rules.length; j++) {
|
||||
let rule = rules[j];
|
||||
if (rule.type === CSSRule.KEYFRAMES_RULE && ((rule as CSSKeyframesRule).name === "slide-right" )) {
|
||||
styleSheet.deleteRule(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// export interface song {
|
||||
// entitiesByUniqueId: {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
export async function getLatestItems(apiUrl: string) {
|
||||
try {
|
||||
const response = await fetch(apiUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`API call failed with status ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (!Array.isArray(data)) {
|
||||
throw new Error('API response is not an array');
|
||||
}
|
||||
const lastFive = data.slice(-5);
|
||||
return lastFive;
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
}
|
||||
}
|
||||
14
src/index.tsx
Normal file
14
src/index.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* @refresh reload */
|
||||
import { render } from 'solid-js/web'
|
||||
|
||||
import App from './App'
|
||||
import ReloadOnBack from './components/events'
|
||||
|
||||
const root = document.getElementById('root')
|
||||
|
||||
render(() => (
|
||||
<>
|
||||
<ReloadOnBack />
|
||||
<App />
|
||||
</>
|
||||
), root!)
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
||||
Loading…
Add table
Add a link
Reference in a new issue