Skip to content

Commit ef4f5be

Browse files
committed
refactor: about me page
1 parent 48712cc commit ef4f5be

File tree

7 files changed

+935
-150
lines changed

7 files changed

+935
-150
lines changed

public/about-me/about.piml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
(title) About Me👨‍💻
1+
(title) About Me
22
(email) samil.bulbul@gmail.com

src/pages/AboutPage.js

Lines changed: 80 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,177 +1,108 @@
11
import React, { useState, useEffect } from 'react';
2-
import ReactMarkdown from 'react-markdown';
3-
import remarkGfm from 'remark-gfm';
4-
import rehypeRaw from 'rehype-raw';
2+
import { motion, AnimatePresence } from 'framer-motion';
53
import { Link } from 'react-router-dom';
64
import {
7-
ArrowLeftIcon,
8-
ArrowSquareOutIcon,
9-
EnvelopeIcon,
5+
ArrowLeft,
6+
Desktop,
7+
Graph,
8+
Terminal,
9+
Article,
1010
} from '@phosphor-icons/react';
11-
import useSeo from '../hooks/useSeo';
12-
import piml from 'piml';
11+
import NeuromancerHUD from './about-views/NeuromancerHUD';
12+
import InteractiveDesk from './about-views/InteractiveDesk';
13+
import MindMapConstellation from './about-views/MindMapConstellation';
14+
import SimpleText from './about-views/SimpleText';
1315
import { useAchievements } from '../context/AchievementContext';
1416

15-
const LinkRenderer = ({ href, children }) => {
16-
const isExternal = href.startsWith('http') || href.startsWith('https');
17+
const ViewSwitcher = ({ currentView, setView }) => {
18+
const views = [
19+
{ id: 'simple', icon: Article, label: 'Simple' },
20+
{ id: 'hud', icon: Terminal, label: 'Terminal' },
21+
{ id: 'desk', icon: Desktop, label: 'Desk' },
22+
{ id: 'map', icon: Graph, label: 'Mind Map' },
23+
];
24+
1725
return (
18-
<a
19-
href={href}
20-
className="text-primary-400 hover:text-primary-600 transition-colors inline-flex items-center gap-1"
21-
target={isExternal ? '_blank' : undefined}
22-
rel={isExternal ? 'noopener noreferrer' : undefined}
23-
>
24-
{children} {isExternal && <ArrowSquareOutIcon className="text-xs" />}
25-
</a>
26+
<div className="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 bg-black/50 backdrop-blur-md p-2 rounded-full border border-white/10 shadow-2xl flex gap-2">
27+
{views.map((view) => (
28+
<button
29+
key={view.id}
30+
onClick={() => setView(view.id)}
31+
className={`relative px-4 py-2 rounded-full flex items-center gap-2 transition-all ${
32+
currentView === view.id
33+
? 'bg-white text-black font-bold shadow-lg'
34+
: 'text-white/60 hover:text-white hover:bg-white/10'
35+
}`}
36+
>
37+
<view.icon size={20} />
38+
<span className="text-sm hidden md:inline">{view.label}</span>
39+
{currentView === view.id && (
40+
<motion.div
41+
layoutId="view-pill"
42+
className="absolute inset-0 bg-white rounded-full mix-blend-difference -z-10"
43+
/>
44+
)}
45+
</button>
46+
))}
47+
</div>
2648
);
2749
};
2850

2951
const AboutPage = () => {
30-
const [content, setContent] = useState('');
31-
const [email, setEmail] = useState('');
32-
const [title, setTitle] = useState('About Me');
33-
const [loading, setLoading] = useState(true);
52+
const [view, setView] = useState('simple');
3453
const { unlockAchievement } = useAchievements();
3554

36-
useSeo({
37-
title: `${title} | Fezcodex`,
38-
description:
39-
'Learn more about Fezcodex, the developer behind this website.',
40-
keywords: [
41-
'Fezcodex',
42-
'about',
43-
'portfolio',
44-
'developer',
45-
'software engineer',
46-
],
47-
ogTitle: `${title} | Fezcodex`,
48-
ogDescription:
49-
'Learn more about Fezcodex, the developer behind this website.',
50-
ogImage: 'https://fezcode.github.io/logo512.png',
51-
twitterCard: 'summary_large_image',
52-
twitterTitle: `${title} | Fezcodex`,
53-
twitterDescription:
54-
'Learn more about Fezcodex, the developer behind this website.',
55-
twitterImage: 'https://fezcode.github.io/logo512.png',
56-
});
57-
5855
useEffect(() => {
5956
unlockAchievement('curious_soul');
60-
const fetchAboutContent = async () => {
61-
try {
62-
const [metaResponse, contentResponse] = await Promise.all([
63-
fetch('/about-me/about.piml'),
64-
fetch('/about-me/about.txt'),
65-
]);
66-
67-
let attributes = {};
68-
if (metaResponse.ok) {
69-
const pimlText = await metaResponse.text();
70-
attributes = piml.parse(pimlText);
71-
} else {
72-
console.error('Failed to fetch about.piml');
73-
}
74-
75-
let body = '';
76-
if (contentResponse.ok) {
77-
body = await contentResponse.text();
78-
} else {
79-
console.error('Failed to fetch about.txt');
80-
}
81-
82-
setTitle(attributes.title || 'About Me');
83-
setEmail(attributes.email || '');
84-
setContent(body);
85-
} catch (err) {
86-
console.error('Error fetching about page content:', err);
87-
} finally {
88-
setLoading(false);
89-
}
57+
// Hide overflow on body when this component is mounted
58+
document.body.style.overflow = 'hidden';
59+
return () => {
60+
document.body.style.overflow = 'auto';
9061
};
91-
92-
fetchAboutContent();
93-
}, []);
94-
95-
if (loading) {
96-
// Skeleton loading screen for AboutPage
97-
return (
98-
<div className="py-16 sm:py-24">
99-
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
100-
<div className="border border-gray-700 p-8 rounded-lg shadow-xl flex">
101-
<div className="w-1 bg-gray-600 mr-1 hidden sm:block"></div>
102-
<div className="w-1 bg-gray-700 mr-1 hidden sm:block"></div>
103-
<div className="w-1 bg-gray-800 mr-8 hidden sm:block"></div>
104-
<div className="flex-grow">
105-
<div className="h-10 bg-gray-800 rounded w-3/4 mb-8 animate-pulse"></div>
106-
<div className="space-y-4">
107-
<div className="h-6 bg-gray-800 rounded w-full animate-pulse"></div>
108-
<div className="h-6 bg-gray-800 rounded w-5/6 animate-pulse"></div>
109-
<div className="h-6 bg-gray-800 rounded w-full animate-pulse"></div>
110-
<div className="h-6 bg-gray-800 rounded w-2/3 animate-pulse"></div>
111-
</div>
112-
<div className="mt-8">
113-
<div className="h-8 bg-gray-800 rounded w-1/4 mb-4 animate-pulse"></div>
114-
<div className="h-6 bg-gray-800 rounded w-1/2 animate-pulse"></div>
115-
</div>
116-
</div>
117-
</div>
118-
</div>
119-
</div>
120-
);
121-
}
62+
}, [unlockAchievement]);
12263

12364
return (
124-
<div className="py-8 sm:py-16">
125-
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
65+
<div className="fixed inset-0 z-[100] bg-black overflow-hidden">
66+
{/* Global Back Button */}
67+
<motion.div
68+
initial={{ opacity: 0, y: -20 }}
69+
animate={{ opacity: 1, y: 0 }}
70+
className="fixed top-6 left-6 z-50"
71+
>
12672
<Link
12773
to="/"
128-
className="group text-primary-400 hover:underline flex items-center justify-center gap-2 text-lg mb-4"
74+
className={`group flex items-center gap-2 px-4 py-2 backdrop-blur-md border transition-all ${
75+
view === 'simple'
76+
? 'bg-transparent text-black border-black border-2 font-mono uppercase tracking-widest text-xs hover:bg-[#4a0404] hover:text-white hover:border-[#4a0404] rounded-none'
77+
: 'bg-black/50 text-white border-white/10 hover:bg-white hover:text-black rounded-full font-bold'
78+
}`}
12979
>
130-
<ArrowLeftIcon className="text-xl transition-transform group-hover:-translate-x-1" />{' '}
131-
Back to Home
80+
<ArrowLeft weight="bold" className="group-hover:-translate-x-1 transition-transform" />
81+
<span className="hidden sm:inline">Back to Reality</span>
13282
</Link>
133-
<div className="border border-gray-700 p-8 rounded-lg shadow-xl flex">
134-
<div className="w-1 bg-gray-600 mr-1 hidden sm:block"></div>
135-
<div className="w-1 bg-gray-700 mr-1 hidden sm:block"></div>
136-
<div className="w-1 bg-gray-800 mr-8 hidden sm:block"></div>
137-
<div className="flex-grow">
138-
<h1 className="text-4xl font-bold tracking-tight text-primary-400 sm:text-6xl mb-8">
139-
{title}
140-
</h1>
83+
</motion.div>
14184

142-
<div className="prose prose-invert max-w-none leading-snug font-mono">
143-
<ReactMarkdown
144-
remarkPlugins={[remarkGfm]}
145-
rehypePlugins={[rehypeRaw]}
146-
components={{ a: LinkRenderer }}
147-
>
148-
{content}
149-
</ReactMarkdown>
150-
</div>
85+
{/* View Container */}
86+
<AnimatePresence mode="wait">
87+
<motion.div
88+
key={view}
89+
initial={{ opacity: 0 }}
90+
animate={{ opacity: 1 }}
91+
exit={{ opacity: 0 }}
92+
transition={{ duration: 0.5 }}
93+
className="w-full h-full"
94+
>
95+
{view === 'hud' && <NeuromancerHUD />}
96+
{view === 'desk' && <InteractiveDesk />}
97+
{view === 'map' && <MindMapConstellation />}
98+
{view === 'simple' && <SimpleText />}
99+
</motion.div>
100+
</AnimatePresence>
151101

152-
{email && (
153-
<div className="mt-8">
154-
<h2 className="text-3xl font-semibold tracking-tight text-white mb-4">
155-
Contact
156-
</h2>
157-
<p className="flex items-center gap-2">
158-
<EnvelopeIcon className="text-primary-400" /> Feel free to
159-
reach out to me at{' '}
160-
<a
161-
href={`mailto:${email}`}
162-
className="text-primary-400 hover:text-primary-500 transition-colors"
163-
>
164-
{email}
165-
</a>
166-
.
167-
</p>
168-
</div>
169-
)}
170-
</div>
171-
</div>
172-
</div>
102+
{/* Switcher Controls */}
103+
<ViewSwitcher currentView={view} setView={setView} />
173104
</div>
174105
);
175106
};
176107

177-
export default AboutPage;
108+
export default AboutPage;

0 commit comments

Comments
 (0)