feat: add GitHub projects and Spotify songs

This commit is contained in:
End 2024-11-30 12:30:05 -06:00
parent db3a264d8a
commit 20bccba46c
No known key found for this signature in database
12 changed files with 245 additions and 1 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.env

View file

@ -24,6 +24,18 @@
<h1>Welcome to my resume website</h1>
<p>This is the homepage. It is currently a WIP.</p>
</main>
<div id="spotify-list">
<h2>My Most Listened to Songs</h2>
<ul id="spotify-list"></ul>
</div>
<div id="github-repos">
<h2>My GitHub Repositories</h2>
<ul id="repo-list"></ul>
</div>
<!-- scripts -->
<script src="/scripts/theme-toggle.js"></script>
<script src="/scripts/spotify.js"></script>
<script src="/scripts/github-repos.js"></script>
</body>
</html>

0
pages/HEID.html Normal file
View file

0
pages/POE.html Normal file
View file

0
pages/about.html Normal file
View file

32
pages/github-repos.html Normal file
View file

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GitHub Projects</title>
<link rel="stylesheet" href="/pages/style.css">
</head>
<body>
<div id="loading">
<div class="spinner"></div>
</div>
<div id="navbar"></div>
<script>
fetch('/pages/navbar.html')
.then(response => response.text())
.then(data => {
document.getElementById('navbar').innerHTML = data;
});
</script>
<main>
<h1>My GitHub Projects</h1>
<ul id="repo-list"></ul>
</main>
<script>
window.addEventListener('load', () => {
document.getElementById('loading').style.display = 'none';
});
</script>
<script src="/scripts/github-repos.js"></script>
</body>
</html>

View file

@ -2,5 +2,7 @@
<ul>
<li><a href="../index.html">Home</a></li>
<li><a href="/pages/APCSP.html">APCSP Project</a></li>
<li><a href="/pages/github-projects.html">GitHub Projects</a></li>
</ul>
<input type="color" id="theme-color-picker" title="Choose your color">
</nav>

View file

@ -21,6 +21,15 @@ body {
--accent-color: #ff5722;
}
[data-theme="light"] {
--background-color: var(--background-color-light);
--text-color: var(--text-color-light);
}
[data-theme="dark"] {
--background-color: var(--background-color-dark);
--text-color: var(--text-color-dark);
}
body {
background-color: var(--background-color);
@ -48,6 +57,14 @@ nav ul li a {
font-weight: bold;
}
/* Theme toggle button */
.theme-toggle {
background: none;
border: none;
font-size: 1.5em;
cursor: pointer;
color: white;
margin-left: auto;
}
/* Header styling */
@ -90,3 +107,59 @@ nav ul li a {
margin-bottom: 10px;
}
/* Light/Dark Mode Toggle */
[data-theme="dark"] body {
background-color: var(--background-color-dark);
color: var(--text-color-dark);
}
[data-theme="light"] body {
background-color: var(--background-color-light);
color: var(--text-color-light);
}
/* responsive design */
@media (max-width: 768px) {
nav ul {
flex-direction: column;
}
.main-content { padding: 10px; }
}
/* Loading animation */
#loading {
position: fixed;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
#loading .spinner {
border: 8px solid #f3f3f3;
border-top: 8px solid var(--primary-color);
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#spotify-list {
position:fixed;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 200px;
background: rgba(255, 255, 255, 0.9);
padding: 10px;
box-sizing: 0 0 10px rgba(0, 0, 0, 0.1);
}

10
scripts/github-repos.js Normal file
View file

@ -0,0 +1,10 @@
fetch('/github-repos')
.then(response => response.json())
.then(data => {
const repoList = document.getElementById('repo-list');
data.forEach(repo => {
const listItem = document.createElement('li');
listItem.innerHTML = `<h3>${repo.name}</h3><p>${repo.readme}</p>`;
repoList.appendChild(listItem);
});
});

10
scripts/spotify.js Normal file
View file

@ -0,0 +1,10 @@
fetch('/spotify-data')
.then(response => response.json())
.then(data => {
const spotifyList = document.getElementById('spotify-list');
data.items.forEach(track => {
const listItem = document.createElement('li');
listItem.textContent = `${track.name} by ${track.artists.map(artist => artist.name).join(', ')}`;
spotifyList.appendChild(listItem);
});
});

13
scripts/theme-toggle.js Normal file
View file

@ -0,0 +1,13 @@
const colorPicker = document.getElementById('theme-color-picker'); // Get the color picker element
// set intial color
const currentColor = localStorage.getItem('theme-color'); || '#3f10ad';
document.documentElement.style.setProperty('--primary-color', currentColor);
colorPicker.value = currentColor;
// update theme color and sae in local storage
colorPicker.addEventListener('input', (event) => {
const newColor = event.target.value;
document.documentElement.style.setProperty('--primary-color', newColor);
localStorage.setItem('theme-color', newColor);
})

90
server.js Normal file
View file

@ -0,0 +1,90 @@
require('dotenv').config();
const express = require('express');
const querystring = require('querystring');
const app = express();
const port = process.env.PORT || 3000;
const client_id = process.env.SPOTIFY_CLIENT_ID;
const client_secret = process.env.SPOTIFY_CLIENT_SECRET;
const redirect_uri = process.env.SPOTIFY_REDIRECT_URI;
let access_token = '';
let refresh_token = '';
app.use(express.static('public'));
app.get('/login', (req, res) => {
const scope = 'user-top-read';
const authUrl = 'https://accounts.spotify.com/authorize?' +
querystring.stringify({
response_type: 'code',
client_id: client_id,
scope: scope,
redirect_uri: redirect_uri
});
res.redirect(authUrl);
});
app.get('/callback', async (req, res) => {
const code = req.query.code || null;
const fetch = (await import('node-fetch')).default;
const tokenResponse = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(`${client_id}:${client_secret}`).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded'
},
body: querystring.stringify({
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
})
});
const tokenData = await tokenResponse.json();
access_token = tokenData.access_token;
refresh_token = tokenData.refresh_token;
res.redirect('/');
});
async function refreshAccessToken() {
const fetch = (await import('node-fetch')).default;
const tokenResponse = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + Buffer.from(`${client_id}:${client_secret}`).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded'
},
body: querystring.stringify({
grant_type: 'refresh_token',
refresh_token: refresh_token
})
});
const tokenData = await tokenResponse.json();
access_token = tokenData.access_token;
}
app.get('/spotify-data', async (req, res) => {
if (!access_token) {
await refreshAccessToken();
}
const fetch = (await import('node-fetch')).default;
const response = await fetch('https://api.spotify.com/v1/me/top/tracks', {
headers: {
'Authorization': `Bearer ${access_token}`
}
});
const data = await response.json();
res.json(data);
});
app.get('/github-repos', async (req, res) => {
const fetch = (await import('node-fetch')).default;
const response = await fetch(`https://api.github.com/users/${process.env.GITHUB_USERNAME}/repos`);
const data = await response.json();
res.json(data);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});