mirror of
https://github.com/yude-jp/yude.jp
synced 2024-12-22 12:10:11 +09:00
Compare commits
17 Commits
d41215438b
...
8b3a484d08
Author | SHA1 | Date | |
---|---|---|---|
8b3a484d08 | |||
e7643e30a8 | |||
cd93e4691d | |||
32064d4ba2 | |||
ebf88ba96b | |||
dc8ccde80c | |||
04abc7d4bf | |||
9ad8943b6d | |||
ff919b2999 | |||
019bf3c4ad | |||
8ad79231a2 | |||
88e7a6abf9 | |||
9f2e0cc407 | |||
c33cebd4c1 | |||
52a21e952b | |||
11073186de | |||
37ac2d2f7a |
3
.env.local.sample
Normal file
3
.env.local.sample
Normal file
@ -0,0 +1,3 @@
|
||||
SPOTIFY_CLIENT_ID=
|
||||
SPOTIFY_CLIENT_SECRET=
|
||||
SPOTIFY_REFRESH_TOKEN=
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,3 +10,4 @@ dist
|
||||
yarn-error.log
|
||||
.vimrc~
|
||||
..vimrc.un~
|
||||
.env.local
|
||||
|
@ -7,7 +7,7 @@ Built with [Next.js](https://nextjs.org/) and [Tailwind CSS](https://tailwindcss
|
||||
|
||||
## Development
|
||||
* To setup your repository, please run `yarn`.
|
||||
* To view this website in your computer, please run `yarn dev`.
|
||||
* To preview, please run `yarn dev`.
|
||||
|
||||
## License
|
||||
This repository is licensed under the MIT License.
|
||||
|
@ -2,5 +2,6 @@
|
||||
"footer": "This page is licensed under the MIT License.",
|
||||
"source": "Source code",
|
||||
"tos": "yude.jp Terms of Service",
|
||||
"yes_playing": "Playing {{playing}}"
|
||||
"yes_playing": "Playing {{playing}}",
|
||||
"listening": "Listening to {{listening}}"
|
||||
}
|
@ -2,5 +2,6 @@
|
||||
"footer": "このページは MIT License の下でライセンスされています。",
|
||||
"source": "ソースコード",
|
||||
"tos": "yude.jp サービス利用規約",
|
||||
"yes_playing": "{{playing}} をプレイ中"
|
||||
"yes_playing": "{{playing}} をプレイ中",
|
||||
"listening": "{{listening}} を再生中"
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
"description": "Front page of yude.jp",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "next -p 30221",
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"export": "next export"
|
||||
@ -27,17 +27,20 @@
|
||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
||||
"@tailwindcss/typography": "^0.4.0",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"axios": "^0.21.1",
|
||||
"next": "^10.2.2",
|
||||
"next-themes": "^0.0.14",
|
||||
"next-translate": "^1.0.7",
|
||||
"popper.js": "^1.16.1",
|
||||
"postcss": "^8.3.0",
|
||||
"querystring": "^0.2.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-markdown": "^6.0.2",
|
||||
"react-switch": "^6.0.0",
|
||||
"remark-gfm": "^1.0.0",
|
||||
"swr": "^0.5.6",
|
||||
"tailwindcss": "^2.1.2",
|
||||
"tailwindcss-filters": "^3.0.0",
|
||||
"tailwindcss-responsive-embed": "^1.0.0",
|
||||
|
58
pages/api/Spotify.js
Normal file
58
pages/api/Spotify.js
Normal file
@ -0,0 +1,58 @@
|
||||
import querystring from 'querystring';
|
||||
|
||||
const {
|
||||
SPOTIFY_CLIENT_ID: client_id,
|
||||
SPOTIFY_CLIENT_SECRET: client_secret,
|
||||
SPOTIFY_REFRESH_TOKEN: refresh_token,
|
||||
} = process.env;
|
||||
|
||||
const basic = Buffer.from(`${client_id}:${client_secret}`).toString('base64');
|
||||
const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;
|
||||
const TOKEN_ENDPOINT = `https://accounts.spotify.com/api/token`;
|
||||
|
||||
const getAccessToken = async () => {
|
||||
const response = await fetch(TOKEN_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Basic ${basic}`,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: querystring.stringify({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token,
|
||||
}),
|
||||
});
|
||||
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const getNowPlaying = async () => {
|
||||
const { access_token } = await getAccessToken();
|
||||
|
||||
return fetch(NOW_PLAYING_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default async (_, res) => {
|
||||
const response = await getNowPlaying();
|
||||
|
||||
if (response.status === 204 || response.status > 400) {
|
||||
return res.status(200).json({ isPlaying: false });
|
||||
}
|
||||
|
||||
const song = await response.json();
|
||||
const isPlaying = song.is_playing;
|
||||
const title = song.item.name;
|
||||
const artist = song.item.artists.map((_artist) => _artist.name).join(', ');
|
||||
const album = song.item.album.name;
|
||||
|
||||
return res.status(200).json({
|
||||
album,
|
||||
artist,
|
||||
isPlaying,
|
||||
title,
|
||||
});
|
||||
};
|
@ -1,26 +1,44 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const url = "https://discord.com/api/guilds/723409709306216498/widget.json";
|
||||
const App = () => {
|
||||
function App (){
|
||||
const router = useRouter()
|
||||
const { locale, locales, defaultLocale, pathname } = router
|
||||
const { t, lang } = useTranslation("common")
|
||||
|
||||
const [playing, setPlaying] = React.useState(0);
|
||||
React.useEffect(() => {
|
||||
fetch(url)
|
||||
.then((r) => r.json())
|
||||
.then((j) => setPlaying(j.members[0].game.name))
|
||||
}, []);
|
||||
const [data, setData] = useState({ hits: [] });
|
||||
useEffect(async () => {
|
||||
const result = await axios(
|
||||
'https://discord.com/api/guilds/723409709306216498/widget.json',
|
||||
);
|
||||
|
||||
const yes_playing = t('yes_playing', {playing: playing})
|
||||
if (playing){
|
||||
return <p>{yes_playing}</p>
|
||||
setData(result.data);
|
||||
}, []);
|
||||
if (data === undefined){
|
||||
console.log("[Discord API] データの取得に失敗しました。 / Failed to retrieve data.")
|
||||
return <p></p>
|
||||
}else{
|
||||
return <p> </p>
|
||||
const str = JSON.stringify(data)
|
||||
|
||||
if (str.indexOf("game") !== -1){
|
||||
const yes_playing = t('yes_playing', {playing: data.members[0].game.name})
|
||||
return <p>{yes_playing}</p>
|
||||
console.log("[Discord API] Playing: " + data.members[0].game.name)
|
||||
}else{
|
||||
return <p></p>
|
||||
console.log("[Discord API] Nothing playing")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function getServerSideProps() {
|
||||
// Fetch data from external API
|
||||
const res = await fetch(url)
|
||||
const data = await res.json()
|
||||
|
||||
// Pass data to the page via props
|
||||
return { props: { data } }
|
||||
}
|
||||
|
||||
export default App;
|
@ -8,7 +8,10 @@ const App = () => {
|
||||
.then((r) => r.json())
|
||||
.then((j) => setStatus(j.members[0].status))
|
||||
}, []);
|
||||
|
||||
if (status === undefined){
|
||||
console.log("[Discord API] オンライン状態を取得できませんでした。 / Failed to retrieve online status.")
|
||||
return <div></div>
|
||||
}else{
|
||||
if (status === "online") {
|
||||
return <div className="font-bold text-gray-700 rounded-full bg-green-500 flex w-5 h-5 items-center justify-center"></div>
|
||||
}else{
|
||||
@ -23,5 +26,6 @@ const App = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default App;
|
@ -24,7 +24,7 @@ const Layout = (props) => {
|
||||
</Head>
|
||||
<main>
|
||||
<Navbar />
|
||||
<div className="md:mx-40 mx-6">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
{children}
|
||||
</div>
|
||||
<Footer />
|
||||
|
33
pages/components/Spotify.js
Normal file
33
pages/components/Spotify.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
function App () {
|
||||
const router = useRouter()
|
||||
const { locale, locales, defaultLocale, pathname } = router
|
||||
const { t, lang } = useTranslation("common")
|
||||
const [data, setData] = useState({ hits: [] });
|
||||
useEffect(async () => {
|
||||
const result = await axios(
|
||||
'/api/Spotify',
|
||||
);
|
||||
|
||||
setData(result.data);
|
||||
}, []);
|
||||
|
||||
if (data === undefined){
|
||||
console.log("[Spotify Web API] データの取得に失敗しました。 / Failed to retrieve data.")
|
||||
return <p></p>
|
||||
}else{
|
||||
if (data.isPlaying){
|
||||
const status = data.artist + ' / ' + data.title
|
||||
const listening = t('listening', {listening: status})
|
||||
return <p>{listening}</p>
|
||||
}else{
|
||||
return <p></p>
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default App;
|
@ -34,7 +34,7 @@ export default function Index(props) {
|
||||
unoptimized = {true}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-5 gap-10 max-w-xl mx-auto">
|
||||
<div className="grid grid-cols-5 gap-10">
|
||||
<div className="has-tooltip"><span className="tooltip rounded shadow-lg p-1 bg-yellow-600 transform translate-y-10 -translate-x-10">{profile}</span><Link href="/profile"><a><FontAwesomeIcon icon={faUser} className="w-10 h-10 fill-current inline transition duration-200 ease-in-out transform hover:-translate-y-1 hover:scale-110" /></a></Link></div>
|
||||
<div className="has-tooltip"><span className="tooltip rounded shadow-lg p-1 bg-yellow-600 transform translate-y-10 -translate-x-9">{blog}</span><Link href="https://blog.yude.jp"><a><FontAwesomeIcon icon={faBlog} className="w-10 h-10 fill-current inline transition duration-200 ease-in-out transform hover:-translate-y-1 hover:scale-110" /></a></Link></div>
|
||||
<div className="has-tooltip"><span className="tooltip rounded shadow-lg p-1 bg-yellow-600 transform translate-y-10 -translate-x-12">{status}</span><Link href="/status"><a><FontAwesomeIcon icon={faServer} className="w-10 h-10 fill-current inline transition duration-200 ease-in-out transform hover:-translate-y-1 hover:scale-110" /></a></Link></div>
|
||||
|
@ -1,13 +1,14 @@
|
||||
import Layout from "./components/Layout"
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
import { faDiscord, faTwitter, faGithub, faKeybase, faInstagram, faMastodon, faSteam } from '@fortawesome/free-brands-svg-icons'
|
||||
import { faEnvelope, faBirthdayCake, faMapPin, faSchool, faPhone, faInfo, faKey, faDownload, faEye } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faEnvelope, faBirthdayCake, faMapPin, faSchool, faPhone, faInfo, faKey, faDownload, faEye, faUserClock } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import DiscordStatus from './components/DiscordStatus'
|
||||
import { useRouter } from 'next/router'
|
||||
import DiscordPlaying from './components/DiscordPlaying'
|
||||
import Spotify from './components/Spotify'
|
||||
|
||||
export default function About(props) {
|
||||
const router = useRouter()
|
||||
@ -55,6 +56,7 @@ export default function About(props) {
|
||||
|
||||
<p className="text-4xl subpixel-antialiased">yude</p>
|
||||
<DiscordPlaying />
|
||||
<Spotify />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -194,7 +196,14 @@ export default function About(props) {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
{
|
||||
// WakaTime
|
||||
}
|
||||
<div className="text-left my-6">
|
||||
<p className="text-2xl"><FontAwesomeIcon icon={faUserClock} className="w-5 h-5 inline"/> WakaTime</p>
|
||||
<figure className="max-w-2xl"><embed src="https://wakatime.com/share/@yude/6f15075a-b9d5-464a-8b4f-545d31933dfb.svg"></embed></figure>
|
||||
<figure className="max-w-2xl"><embed src="https://wakatime.com/share/@yude/a8c52934-488b-4bdd-aed1-4f3fc73eb78e.svg"></embed></figure>
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -13,7 +13,9 @@
|
||||
@apply list-disc mx-10
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@apply hover:underline
|
||||
}
|
||||
.tooltip {
|
||||
@apply invisible absolute;
|
||||
}
|
||||
|
26
yarn.lock
26
yarn.lock
@ -549,6 +549,13 @@ available-typed-arrays@^1.0.2:
|
||||
dependencies:
|
||||
array.prototype.filter "^1.0.0"
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||
dependencies:
|
||||
follow-redirects "^1.10.0"
|
||||
|
||||
babel-plugin-inline-react-svg@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-inline-react-svg/-/babel-plugin-inline-react-svg-2.0.1.tgz#68c9c119d643a8f2d7bf939b942534d89ae3ade9"
|
||||
@ -1081,6 +1088,11 @@ depd@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
|
||||
|
||||
dequal@2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
|
||||
integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==
|
||||
|
||||
des.js@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
|
||||
@ -1372,6 +1384,11 @@ find-up@^4.0.0:
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
follow-redirects@^1.10.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
|
||||
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
|
||||
|
||||
foreach@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
||||
@ -2716,7 +2733,7 @@ querystring@0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||
|
||||
querystring@^0.2.0:
|
||||
querystring@^0.2.0, querystring@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
|
||||
integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==
|
||||
@ -3208,6 +3225,13 @@ svgo@^2.0.3:
|
||||
csso "^4.2.0"
|
||||
stable "^0.1.8"
|
||||
|
||||
swr@^0.5.6:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/swr/-/swr-0.5.6.tgz#70bfe9bc9d7ac49a064be4a0f4acf57982e55a31"
|
||||
integrity sha512-Bmx3L4geMZjYT5S2Z6EE6/5Cx6v1Ka0LhqZKq8d6WL2eu9y6gHWz3dUzfIK/ymZVHVfwT/EweFXiYGgfifei3w==
|
||||
dependencies:
|
||||
dequal "2.0.2"
|
||||
|
||||
tailwindcss-aspect-ratio@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss-aspect-ratio/-/tailwindcss-aspect-ratio-3.0.0.tgz#4f9fc7ca0f3468373290faaa8b92652157bee6a4"
|
||||
|
Loading…
Reference in New Issue
Block a user