Skip to content

Commit 3583b92

Browse files
committed
Initial Commit
1 parent 9c89198 commit 3583b92

16 files changed

Lines changed: 828 additions & 26 deletions

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"lucide-react": "^1.8.0",
1314
"react": "^19.2.4",
1415
"react-dom": "^19.2.4",
1516
"react-router": "^7.14.1"

public/alien_glax.png

762 KB
Loading

public/alien_vex.png

786 KB
Loading

public/alien_zorblax.png

823 KB
Loading

src/App.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
1-
import { Routes, Route } from "react-router";
2-
import Home from "./pages/home/Home";
3-
import ApiExample from "./pages/api-example/ApiExample";
1+
import { Routes, Route, Link } from 'react-router';
2+
import Signup from './pages/Signup';
3+
import Explore from './pages/Explore';
4+
import Chat from './pages/Chat';
5+
import { Rocket } from 'lucide-react';
46

5-
// Defines which URL path corresponds to which page component.
67
function App() {
78
return (
8-
<Routes>
9-
<Route path="/" element={<Home />} />
10-
<Route path="/api-example" element={<ApiExample />} />
11-
</Routes>
9+
<div className="app-container">
10+
<nav className="navbar">
11+
<Link to="/" className="navbar-brand">
12+
<Rocket className="inline-block mr-2" size={24} style={{ verticalAlign: 'text-bottom', color: 'var(--color-secondary)' }} />
13+
SSTRUK
14+
</Link>
15+
<div style={{ display: 'flex', gap: '16px' }}>
16+
<Link to="/explore" className="btn-outline" style={{ padding: '6px 16px', fontSize: '0.9rem' }}>Explore</Link>
17+
</div>
18+
</nav>
19+
20+
<main className="main-content">
21+
<Routes>
22+
<Route path="/" element={<Signup />} />
23+
<Route path="/explore" element={<Explore />} />
24+
<Route path="/chat/:id" element={<Chat />} />
25+
</Routes>
26+
</main>
27+
</div>
1228
);
1329
}
1430

src/components/MatchOverlay.tsx

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { useEffect } from 'react';
2+
import type { AlienProfile } from '../data/mockAliens';
3+
import { useNavigate } from 'react-router';
4+
import { Heart } from 'lucide-react';
5+
6+
interface MatchOverlayProps {
7+
alien: AlienProfile;
8+
userName: string;
9+
}
10+
11+
export default function MatchOverlay({ alien, userName }: MatchOverlayProps) {
12+
const navigate = useNavigate();
13+
14+
useEffect(() => {
15+
// Automatically redirect to chat after 3 seconds
16+
const timer = setTimeout(() => {
17+
navigate(`/chat/${alien.id}`);
18+
}, 3000);
19+
return () => clearTimeout(timer);
20+
}, [navigate, alien.id]);
21+
22+
return (
23+
<div style={{
24+
position: 'fixed',
25+
top: 0, left: 0, right: 0, bottom: 0,
26+
backgroundColor: 'rgba(130, 2, 99, 0.9)',
27+
backdropFilter: 'blur(10px)',
28+
display: 'flex',
29+
flexDirection: 'column',
30+
alignItems: 'center',
31+
justifyContent: 'center',
32+
zIndex: 2000,
33+
color: 'white',
34+
animation: 'fadeIn 0.5s ease-out'
35+
}}>
36+
<style>{`
37+
@keyframes fadeIn {
38+
from { opacity: 0; transform: scale(0.9); }
39+
to { opacity: 1; transform: scale(1); }
40+
}
41+
@keyframes pulseHeart {
42+
0% { transform: scale(1); }
43+
50% { transform: scale(1.3); }
44+
100% { transform: scale(1); }
45+
}
46+
`}</style>
47+
48+
<h1 style={{ fontSize: '4rem', fontStyle: 'italic', marginBottom: '40px', textShadow: '0 4px 20px rgba(0,0,0,0.5)' }}>
49+
IT'S A MATCH!
50+
</h1>
51+
52+
<div style={{ display: 'flex', alignItems: 'center', gap: '32px', marginBottom: '40px' }}>
53+
<div style={{
54+
width: '120px', height: '120px', borderRadius: '50%',
55+
background: 'var(--color-dark)', border: '4px solid var(--color-white)',
56+
display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '2rem', fontWeight: 'bold'
57+
}}>
58+
{userName.substring(0, 1).toUpperCase()}
59+
</div>
60+
61+
<Heart size={64} fill="var(--color-secondary)" style={{ animation: 'pulseHeart 1s infinite' }} />
62+
63+
<img
64+
src={alien.profilePic}
65+
alt={alien.name}
66+
style={{
67+
width: '120px', height: '120px', borderRadius: '50%',
68+
objectFit: 'cover', border: '4px solid var(--color-white)',
69+
boxShadow: '0 0 30px rgba(234, 222, 218, 0.5)'
70+
}}
71+
/>
72+
</div>
73+
74+
<p style={{ fontSize: '1.5rem', opacity: 0.9 }}>
75+
Opening transmission to {alien.name}...
76+
</p>
77+
</div>
78+
);
79+
}

src/components/OrbitSystem.tsx

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { useState } from 'react';
2+
import { mockAliens } from '../data/mockAliens';
3+
import type { AlienProfile } from '../data/mockAliens';
4+
import { useAppContext } from '../context/AppContext';
5+
import ProfileModal from './ProfileModal';
6+
import MatchOverlay from './MatchOverlay';
7+
8+
export default function OrbitSystem() {
9+
const { preferences, addMatch } = useAppContext();
10+
const [selectedAlien, setSelectedAlien] = useState<AlienProfile | null>(null);
11+
const [matchedAlien, setMatchedAlien] = useState<AlienProfile | null>(null);
12+
13+
if (!preferences) return null;
14+
15+
// Filter aliens based on max distance
16+
const visibleAliens = mockAliens.filter(a => a.distanceAU <= preferences.maxDistanceAU);
17+
18+
const handleMatch = (alien: AlienProfile) => {
19+
addMatch(alien);
20+
setSelectedAlien(null);
21+
setMatchedAlien(alien);
22+
};
23+
24+
return (
25+
<>
26+
<style>{`
27+
@keyframes orbit {
28+
from { transform: rotate(0deg) translateX(var(--radius)) rotate(0deg); }
29+
to { transform: rotate(360deg) translateX(var(--radius)) rotate(-360deg); }
30+
}
31+
.orbit-ring {
32+
position: absolute;
33+
top: 50%;
34+
left: 50%;
35+
border: 1px dashed rgba(217, 3, 104, 0.2);
36+
border-radius: 50%;
37+
transform: translate(-50%, -50%);
38+
pointer-events: none;
39+
}
40+
.orbit-item {
41+
position: absolute;
42+
top: 50%;
43+
left: 50%;
44+
margin-top: -30px;
45+
margin-left: -30px;
46+
width: 60px;
47+
height: 60px;
48+
border-radius: 50%;
49+
cursor: pointer;
50+
transition: transform 0.2s;
51+
box-shadow: 0 0 15px rgba(234, 222, 218, 0.2);
52+
border: 2px solid var(--color-primary);
53+
background-size: cover;
54+
background-position: center;
55+
animation: orbit var(--duration) linear infinite;
56+
}
57+
.orbit-item:hover {
58+
transform: scale(1.2) !important;
59+
z-index: 50;
60+
box-shadow: 0 0 25px var(--color-secondary);
61+
animation-play-state: paused;
62+
}
63+
`}</style>
64+
65+
<div style={{ position: 'relative', width: '100%', maxWidth: '800px', height: '600px', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
66+
67+
{/* User Center */}
68+
<div style={{
69+
width: '80px',
70+
height: '80px',
71+
borderRadius: '50%',
72+
background: 'linear-gradient(135deg, var(--color-primary), var(--color-secondary))',
73+
boxShadow: '0 0 30px var(--color-secondary)',
74+
display: 'flex',
75+
alignItems: 'center',
76+
justifyContent: 'center',
77+
fontWeight: 'bold',
78+
zIndex: 10,
79+
fontSize: '1.2rem',
80+
color: 'white'
81+
}}>
82+
{preferences.name.substring(0, 2).toUpperCase() || 'YOU'}
83+
</div>
84+
85+
{/* Orbit Rings and Aliens */}
86+
{visibleAliens.map((alien, i) => {
87+
// Calculate radius based on distance (min 80px, max 280px)
88+
const radiusRatio = preferences.maxDistanceAU > 0 ? alien.distanceAU / preferences.maxDistanceAU : 1;
89+
const radius = 80 + (radiusRatio * 200);
90+
const duration = 15 + (radius / 10); // Slower orbit for further objects
91+
const startAngle = (i * (360 / visibleAliens.length));
92+
93+
return (
94+
<div key={alien.id}>
95+
{/* Ring */}
96+
<div className="orbit-ring" style={{ width: `${radius * 2}px`, height: `${radius * 2}px` }} />
97+
98+
{/* Profile */}
99+
<div
100+
className="orbit-item"
101+
onClick={() => setSelectedAlien(alien)}
102+
style={{
103+
backgroundImage: `url(${alien.profilePic})`,
104+
// @ts-ignore
105+
'--radius': `${radius}px`,
106+
'--duration': `${duration}s`,
107+
animationDelay: `-${startAngle}s` // Stagger start positions
108+
}}
109+
title={`${alien.name} (${alien.distanceAU} AU)`}
110+
/>
111+
</div>
112+
);
113+
})}
114+
</div>
115+
116+
{selectedAlien && (
117+
<ProfileModal
118+
alien={selectedAlien}
119+
onClose={() => setSelectedAlien(null)}
120+
onMatch={handleMatch}
121+
/>
122+
)}
123+
124+
{matchedAlien && (
125+
<MatchOverlay
126+
alien={matchedAlien}
127+
userName={preferences.name || 'User'}
128+
/>
129+
)}
130+
</>
131+
);
132+
}

0 commit comments

Comments
 (0)