This commit is contained in:
arch0Xd 2022-08-14 17:33:43 +02:00
parent 3a92d65900
commit af344d5fcd
32 changed files with 2604 additions and 2724 deletions

View file

@ -1,6 +0,0 @@
{
"extends": "next/core-web-vitals",
"rules": {
"@next/next/no-img-element": "off"
}
}

3
.gitignore vendored
View file

@ -34,5 +34,4 @@ yarn-error.log*
# typescript
*.tsbuildinfo
.vercel
next-env.d.ts

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 skidoodle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,2 +0,0 @@
# portfolio-v2
Built with Next.js ♥

View file

@ -1,11 +0,0 @@
import styles from 'styles/Home.module.scss'
const Body = ({children}: {children: any}) => {
return(
<div className={styles.bodySection}>
{children}
</div>
)
}
export default Body

11
components/Footer.tsx Normal file
View file

@ -0,0 +1,11 @@
export const Footer = () => {
return(
<div className='text-center text-lg'>
<footer className='fixed bottom-2 left-0 right-0 text-[#727277] font-thin'>
<a href='https://arch.gay' target='_blank'>Made by
<span className='gradient font-semibold'> arch</span>
</a>
</footer>
</div>
)
}

View file

@ -1,35 +1,30 @@
import toast, { Toaster } from 'react-hot-toast';
import Link from 'next/link'
import toast from 'react-hot-toast'
import copy from 'copy-to-clipboard'
const Icon = ({icon, reference, copy = false} : {icon: any, reference: any, copy?: boolean}) => {
return(
<>
{
copy ? (
<a href='javascript:void(0)' onClick={() => doThings(reference)}>
{icon}
</a>
) : (
<a href={reference} target='_blank' rel='noopener noreferrer' aria-label="Icon">
{icon}
</a>
)
}
<Toaster position='bottom-center' reverseOrder={false} />
</>
)
type Icon = {
children: any,
reference: string,
copyValue?: boolean,
}
const doThings = (value: any) => {
copy(value)
toast.remove()
toast.success('Copied to clipboard', {
const notify = () => toast.success('Copied to clipboard', {
style: {
background: '#111',
background: '#0f1012',
color: '#fff',
fontSize: '1em'
}
})
export const Icon = ({ children, reference, copyValue }: Icon) => {
if(copyValue) {
return <a className={`cursor-pointer`} onClick={ () => { notify(), copy(reference) }} >{ children }</a>
}
export default Icon
return (
<Link href={ reference }>
<a target='_blank' className={`cursor-pointer`}>{ children }</a>
</Link>
)
}

View file

@ -1,13 +0,0 @@
import styles from 'styles/Home.module.scss'
const IconLayout = ({children}: {children: any}) => {
return(
<>
<div className={styles.iconLayout}>
{children}
</div>
</>
)
}
export default IconLayout

View file

@ -1,14 +0,0 @@
import styles from 'styles/Home.module.scss'
const MainLayout = () => {
return(
<>
<div className={styles.mainLayout}>
<h1>albert</h1>
<p>18-year-old <b>system administrator</b> and student from Hungary</p>
</div>
</>
)
}
export default MainLayout

View file

@ -1,31 +0,0 @@
import { useLastFM } from 'use-last-fm'
import { FaSpotify } from 'react-icons/fa'
import styles from 'styles/Home.module.scss'
const Spotify = () => {
const lastFM = useLastFM('albrtadam', 'f6d19bc25ca340225c70c3d386cd4eab');
return(
<div className={styles.spotify}>
{lastFM.status === 'playing'
?
<>
<a href={lastFM.song.url} target='_blank' rel='noopener noreferrer'>
<img src={lastFM.song.art} width={50} height={50} alt=""/>
<p>Listening to <b>{lastFM.song.name}</b></p>
</a>
</>
:
<>
<img src='song_art.png' width={50} height={50} alt=""/>
<p>Not listening to anything</p>
</>
}
<div className={styles.tinyText}>
<FaSpotify /> Spotify
</div>
</div>
)
}
export default Spotify

View file

@ -1,26 +0,0 @@
import { FaClock } from 'react-icons/fa';
import { dayjs } from 'components/dayjs'
import { useEffect, useState } from 'react'
import styles from 'styles/Home.module.scss'
const now = () => dayjs().tz()
const format = 'hhA'
const Time = () => {
const [date, setDate] = useState(now())
useEffect(() => {
const timer = setInterval(() => setDate(now()), 1000)
return () => clearInterval(timer)
}, [])
return (
<p className={styles.time}>
<FaClock />
{' '}{date.format('DD/MM/YYYY • h:mm:ss A')}
</p>
)
}
export default Time

View file

@ -1,28 +0,0 @@
import styles from 'styles/Home.module.scss'
import { FaSun, FaMoon, FaCloudSun, FaCloudMoon, FaCloud, FaCloudShowersHeavy } from 'react-icons/fa'
import { BsCloudDrizzleFill, BsCloudsFill, BsCloudLightningFill, BsCloudSnowFill, BsCloudFogFill } from 'react-icons/bs'
const Weather = ({data}: {data: any}) => {
const { temp: temperature } = data.main
const { icon: weatherIcon, description: weatherDescription} = data.weather[0]
const icons: any = {
_01d: <FaSun />, _01n: <FaMoon />,
_02d: <FaCloudSun />, _02n: <FaCloudMoon />,
_03d: <FaCloud />, _03n: <FaCloud />,
_04d: <BsCloudsFill />, _04n: <BsCloudsFill />,
_09d: <BsCloudDrizzleFill />, _09n: <BsCloudDrizzleFill />,
_10d: <FaCloudShowersHeavy />, _10n: <FaCloudShowersHeavy />,
_11d: <BsCloudLightningFill />, _11n: <BsCloudLightningFill />,
_13d: <BsCloudSnowFill />, _13n: <BsCloudSnowFill />,
_50d: <BsCloudFogFill />, _50n: <BsCloudFogFill />
}
return (
<div className={styles.weather}>
{icons[`_${weatherIcon}`]} <p>It&apos;s currently <b>{parseInt(temperature)} °C</b> <span>({weatherDescription})</span> in <a href='https://weather.com/en-GB/weather/today/l/b979f874d2f515646f37e2bb434a85cc04869c5a35c6bdf1c6fba26f659313f0' target="_blank" rel='noopener noreferrer'><b>Budapest</b></a></p>
</div>
)
}
export default Weather

View file

@ -0,0 +1,39 @@
import { IconType } from 'react-icons/lib'
import { FaDiscord, FaEnvelope, FaGithub, FaInstagram, FaSteam } from 'react-icons/fa'
type Socials = {
id: number,
ref: string
icon: IconType,
copyValue?: boolean,
}
export const socials: Array<Socials> = [
{
id: 1,
ref: 'https://github.com/skidoodle',
icon: FaGithub,
},
{
id: 2,
ref: 'https://steamcommunity.com/id/_albert',
icon: FaSteam,
},
{
id: 3,
ref: 'contact@albert.lol',
icon: FaEnvelope,
copyValue: true,
},
{
id: 4,
ref: 'albert#8838',
icon: FaDiscord,
copyValue: true,
},
{
id: 5,
ref: 'https://www.instagram.com/albertadam_/',
icon: FaInstagram,
},
]

View file

@ -1,9 +0,0 @@
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.tz.setDefault('Europe/Budapest')
export { dayjs }

View file

@ -1,9 +1,10 @@
const path = require('path')
module.export = {
productionBrowserSourceMaps: true,
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
sassOptions: {
includePaths: [path.join(__dirname, 'styles')]
images: {
domains: ['i.arch.gay', 'cdn.discordapp.com']
}
}
module.exports = nextConfig

2308
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,39 +1,29 @@
{
"name": "portfolio",
"version": "1.0",
"name": "albertfasz",
"version": "0.1.0",
"private": true,
"license": "MIT",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"buildexport": "next build && next export"
"lint": "next lint"
},
"dependencies": {
"aws-sdk": "^2.1189.0",
"copy-to-clipboard": "^3.3.2",
"@swc/core": "^1.2.223",
"csstype": "^3.1.0",
"dayjs": "^1.11.4",
"dotenv": "^16.0.1",
"goober": "2.1.10",
"next": "12.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-fade-in": "^2.0.1",
"react-hot-toast": "^2.3.0",
"react-icons": "^4.4.0",
"sass": "^1.54.3",
"use-last-fm": "^0.6.1"
"next": "12.2.5",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@types/react-dom": "^18.0.6",
"@types/node": "18.6.4",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.4",
"eslint": "8.21.0",
"eslint-config-next": "12.2.4",
"@types/node": "18.7.3",
"@types/react": "18.0.17",
"@types/react-dom": "18.0.6",
"autoprefixer": "^10.4.8",
"copy-to-clipboard": "^3.3.2",
"postcss": "^8.4.16",
"react-hot-toast": "^2.3.0",
"react-icons": "^4.4.0",
"sass": "^1.54.4",
"tailwindcss": "^3.1.8",
"typescript": "4.7.4"
}
}

View file

@ -1,8 +1,15 @@
import { AppProps } from 'next/app'
import 'styles/globals.scss'
import Head from 'next/head'
const MyApp = ({ Component, pageProps }: AppProps) => {
return <Component {...pageProps} />
import { AppProps } from 'next/app'
export default function({ Component, pageProps }: AppProps) {
return(
<>
<Head>
<title>albert</title>
</Head>
<Component {...pageProps} />
</>
)
}
export default MyApp

View file

@ -1,24 +0,0 @@
import { Html, Head, Main, NextScript } from 'next/document'
const Document = () => {
return (
<>
<Html lang='zxx'>
<Head>
<link rel='preconnect' href='https://vitals.vercel-insights.com' />
<link rel='preconnect' href='https://ws.audioscrobbler.com' />
<meta name='title' content='albert' />
<meta name='og:title' content='albert' />
<meta name='description' content='system administrator' />
<meta name='og:description' content='system administrator' />
<meta name='theme-color' content='#000000' />
<meta property='og:image' content='/favicon.ico' />
</Head>
<Main />
<NextScript />
</Html>
</>
)
}
export default Document

39
pages/_error.tsx Normal file
View file

@ -0,0 +1,39 @@
import { GetServerSideProps } from 'next'
type ErrorPage = {
statusCode: number,
message: string
}
export default function({ statusCode, message }: ErrorPage) {
return(
<div className='flex flex-col justify-center items-center h-[90vh]'>
<div>
<h1 className='font-semibold text-2xl inline-block mr-[1.7rem] pr-[1.5rem] border-r-[1px] border-white'>{ statusCode }</h1>
<div className='inline-block text-left'>
<h2 className='text-lg font-extralight'>{ message }</h2>
</div>
</div>
</div>
)
}
export const getServerSideProps: GetServerSideProps = async(props) => {
const { res, err }: any = props
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
var entries: any = {
404: 'Az oldal nem található',
400: 'Érvénytelen kérelem',
500: 'Szerveroldali hiba'
}
const message = entries[statusCode]
return {
props: {
statusCode,
message
}
}
}

View file

@ -1,44 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import aws from 'aws-sdk'
const { BUCKET, ACCESS_KEY, SECRET_KEY, ENDPOINT, REGION } = process.env
export default async function(req: NextApiRequest, res: NextApiResponse) {
aws.config.s3 = ({
accessKeyId: ACCESS_KEY,
secretAccessKey: SECRET_KEY,
region: REGION,
endpoint: ENDPOINT,
signatureVersion: 'v4'
})
let isTruncated: boolean | undefined = true
let startAfter
let objects = 0
let size = 0
const s3 = new aws.S3()
while(isTruncated) {
let params: any = { Bucket: BUCKET }
if(startAfter) {
params.StartAfter = startAfter
}
const data = await s3.listObjectsV2(params).promise()
data.Contents?.forEach((object: any) => {
objects++
size += object.Size! / 1024 / 1024 / 1024
})
isTruncated = data.IsTruncated
if (isTruncated) {
startAfter = data.Contents!.slice(-1)[0].Key;
}
}
res.setHeader('Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59');
res.json({ object: objects, size: Number(size.toFixed(2)) })
}

28
pages/api/spotify.ts Normal file
View file

@ -0,0 +1,28 @@
import { NextApiRequest, NextApiResponse } from 'next'
export default async function(req: NextApiRequest, res: NextApiResponse) {
// archról másolva: https://github.com/arch0Xd/arch.gay
const { LASTFM_USERNAME, LASTFM_API } = process.env
const { recenttracks: response } = await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${LASTFM_USERNAME}&api_key=${LASTFM_API}&format=json&limit=1`).then((res) => res.json())
const { track } = response
const { artist, name, url, image } = track[0]
let nowplaying = Boolean(track[0]['@attr']?.nowplaying)
if(nowplaying) {
res.status(200).json({
nowplaying,
song: {
artist: artist['#text'],
title: name,
url: url,
image: image[2]['#text'],
}
})
}
res.status(200).json({ nowplaying })
}

View file

@ -1,52 +1,57 @@
import { GetServerSideProps } from 'next'
import Head from 'next/head'
import FadeIn from 'react-fade-in'
import Body from 'components/Body'
import Icon from 'components/Icon'
import IconLayout from 'components/IconLayout'
import MainLayout from 'components/MainLayout'
import Spotify from 'components/Spotify'
import Weather from 'components/Weather'
import { FaSteam, FaGithub, FaEnvelope } from 'react-icons/fa'
import { RiInstagramFill } from 'react-icons/ri'
import { SiDiscord } from 'react-icons/si'
import dynamic from 'next/dynamic'
const Time = dynamic(() => import('components/Time'), {
ssr: false,
})
import Image from 'next/image'
import React from 'react'
const Home = ({data}: any) => {
import { socials } from 'components/data/socials'
import { Icon } from 'components/Icon'
import { Toaster } from 'react-hot-toast'
import { GetServerSideProps } from 'next'
import { FaSpotify } from 'react-icons/fa'
import { Footer } from 'components/Footer'
export default function({ spotify }: any) {
return(
<>
<Head>
<title>albert</title>
</Head>
<Body>
<FadeIn>
<MainLayout />
<IconLayout>
<Icon icon={<FaGithub />} reference={'https://github.com/skidoodle'} copy={false} />
<Icon icon={<FaSteam />} reference={'https://steamcommunity.com/id/_albert'} copy={false} />
<Icon icon={<FaEnvelope />} reference={'contact@albert.lol'} copy={true} />
<Icon icon={<RiInstagramFill />} reference={'https://instagram.com/albertadam_'} copy={false} />
<Icon icon={<SiDiscord />} reference={'albert#8838'} copy={true} />
</IconLayout>
<Time />
<Weather data={data} />
<Spotify />
</FadeIn>
</Body>
<div className='px-8 w-11/12 m-auto rounded-lg max-w-4xl'>
<div className='flex flex-col justify-center items-center mt-32 md:mt-56'>
<Image src='https://cdn.discordapp.com/avatars/637745537369767936/9cc2e60b7df282b5be9fa701660c651d.webp?size=512' className="rounded-full text-center" height={100} width={100}/>
<h1 className='text-4xl font-bold -mt-1'>albert</h1>
<p className='text-[#9ca3af] text-xl flex flex-wrap items-center justify-center whitespace-pre-wrap'>
{ Math.floor((new Date().getTime() - new Date('2004.07.22').getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }
yrs old <b className='font-semibold'>system administrator</b> and student from <b className='font-bold'>Hungary</b>
</p>
</div>
<hr className='border-t-[#727277] w-4/5 md:w-2/5 m-auto mt-5 md:mt-8'/>
<div className='mt-3 flex justify-center items-center'>
<FaSpotify className='text-[#32a866]' />&nbsp;
<p className='font-semibold'>Listening to
<span className='text-[#32a866]'> { spotify.song?.artist - spotify.song?.title || 'nothing' }</span>
</p>
</div>
<div className='flex justify-between items-center text-3xl mt-11 md:mt-16 max-w-sm m-auto'>
{ socials.map(social => (
<Icon key={ social.id } reference={ social.ref } copyValue={ social.copyValue }>{ React.createElement(social.icon) }</Icon>
))}
</div>
</div>
<Footer />
<Toaster />
</>
)
}
export const getServerSideProps: GetServerSideProps = async () => {
const response = await fetch('https://api.openweathermap.org/data/2.5/weather?lat=47.51&lon=19.04&appid=1b3c10c18e894eaf1fd63eedde53fa54&units=metric')
const data = await response.json()
const spotify = await fetch(`${process.env.PRODUCTION}/api/spotify`, {
method: 'GET'
}).then((res) => res.json())
return {
props: { data }
props: { spotify }
}
}
export default Home

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

1
public/country.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#EEE" d="M0 14h36v8H0z"/><path fill="#CD2A3E" d="M32 5H4C1.791 5 0 6.791 0 9v5h36V9c0-2.209-1.791-4-4-4z"/><path fill="#436F4D" d="M4 31h28c2.209 0 4-1.791 4-4v-5H0v5c0 2.209 1.791 4 4 4z"/></svg>

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -1,128 +0,0 @@
@import '_variables';
.bodySection {
margin-top: 8rem;
margin-left: 10%;
margin-right: 10%;
}
.mainLayout {
h1 {
line-height: .2;
font-size: 3rem;
font-weight: 700;
}
p {
color: $secondary-color;
font-size: 1rem;
font-weight: 400;
}
}
.iconLayout {
margin-bottom: 5%;
svg {
font-size: 1.4em;
cursor: pointer;
margin-right: 6%;
margin-bottom: 1em;
}
}
.time {
svg {
font-size: .9em;
}
}
.weather {
img {
float: left;
}
p {
display: inline;
}
span {
font-size: .8rem;
}
a:hover {
text-decoration: none;
color: #C7D2FE;
}
svg {
font-size: .9em;
}
}
.spotify {
font-size: 1em;
margin-top: 5%;
padding: 1rem 1rem 1.8rem 1rem;
border: 1px solid $spotify-box-color;
border-radius: .5rem;
img {
border-radius: 10px;
float: left;
margin-right: 5%;
}
p {
display: inline;
}
.tinyText {
font-weight: 300;
font-size: .8em;
svg {
margin-top: 5px;
color: #1DB954;
}
}
}
.footerLayout {
position: absolute;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
a {
cursor: pointer;
text-decoration: none;
&:hover {
color: #C7D2FE
}
}
}
@media (min-width: 1200px) {
.bodySection {
margin-left: 30%;
margin-right: 45%;
}
.iconLayout {
margin-bottom: 2%;
}
.spotify {
margin-top: 8%;
width: 80%;
}
}
@media (min-height: 800px) {
.bodySection {
margin-top: 15rem;
}
}

View file

@ -1,6 +0,0 @@
$background-color: #000000;
$primary-color: #ffffff;
$secondary-color: #9CA3AF;
$selection-color: #8039e2;
$spotify-box-color: #521c9e;
$font: 'Inter', sans-serif;

View file

@ -1,26 +1,56 @@
@import '_variables';
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
scrollbar-width: thin;
scrollbar-color: #8a58e0 transparent;
}
html,
body {
background-color: $background-color;
color: $primary-color;
padding: 0;
margin: 0;
font-family: $font;
font-weight: 400;
background-color: #000;
color: #fff;
}
ul, ol {
list-style: revert;
}
}
@layer components {
::selection {
background: rgba($selection-color, .7);
color: $primary-color;
background-color: #9f36e0;
color: #fff;
}
a {
color: inherit;
text-decoration: none;
::-webkit-scrollbar {
width: 3px;
}
* {
box-sizing: border-box;
::-webkit-scrollbar-thumb {
background-color: #8a58e0;
border-radius: 10px;
}
}
@layer utilities {
.gradient {
color: transparent;
background-clip: text;
background-image: linear-gradient(to right, #800a18, #a82044, #9e4a60);
animation: gradient 5s ease infinite
}
@keyframes gradient {
0%, 100% {
background-size: 200%;
background-position: left center;
}
50% {
background-size: 200%;
background-position: right center;
}
}
}

11
tailwind.config.js Normal file
View file

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
plugins: [],
}

2227
yarn.lock

File diff suppressed because it is too large Load diff