Skip to content

Commit 3c388a7

Browse files
committed
feat: overhaul orbit system and expand alien database
1 parent 1eec998 commit 3c388a7

12 files changed

Lines changed: 200 additions & 47 deletions

public/alien_cyra.jpeg

7.77 KB
Loading

public/alien_ignis.jpeg

7.15 KB
Loading

public/alien_kaelen.jpeg

11.6 KB
Loading

public/alien_lumina.webp

1.3 MB
Loading

public/alien_sparky.jpeg

6.56 KB
Loading

public/alien_squish.webp

1.24 MB
Loading

public/alien_xeno.webp

236 KB
Loading

public/alien_zarok.webp

58.4 KB
Loading

src/components/OrbitSystem.tsx

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,80 @@
1-
import { useState } from 'react';
1+
import { useState, useEffect } from 'react';
22
import { mockAliens } from '../data/mockAliens';
33
import type { AlienProfile } from '../data/mockAliens';
44
import { useAppContext } from '../context/AppContext';
55
import ProfileModal from './ProfileModal';
66
import MatchOverlay from './MatchOverlay';
77

88
export default function OrbitSystem() {
9-
const { preferences, addMatch } = useAppContext();
9+
const { preferences, addMatch, matches } = useAppContext();
1010
const [selectedAlien, setSelectedAlien] = useState<AlienProfile | null>(null);
1111
const [matchedAlien, setMatchedAlien] = useState<AlienProfile | null>(null);
12+
const [dismissedIds, setDismissedIds] = useState<Set<string>>(new Set());
13+
14+
// Keep exactly 5 slots for the 5 orbit tracks
15+
const [activeIds, setActiveIds] = useState<(string | null)[]>([null, null, null, null, null]);
16+
17+
useEffect(() => {
18+
if (!preferences) return;
19+
20+
// Available aliens are those within distance, not matched, not dismissed, and not currently active
21+
const available = mockAliens.filter(a =>
22+
a.distanceAU <= preferences.maxDistanceAU &&
23+
!matches.find(m => m.id === a.id) &&
24+
!dismissedIds.has(a.id) &&
25+
!activeIds.includes(a.id)
26+
);
27+
28+
let changed = false;
29+
const newActiveIds = [...activeIds];
30+
31+
// Check each of the 5 tracks
32+
for (let i = 0; i < 5; i++) {
33+
const currentId = newActiveIds[i];
34+
// Check if the current alien in this track is still valid
35+
const isStillValid = currentId &&
36+
mockAliens.find(a => a.id === currentId && a.distanceAU <= preferences.maxDistanceAU) &&
37+
!matches.find(m => m.id === currentId) &&
38+
!dismissedIds.has(currentId);
39+
40+
if (!isStillValid) {
41+
// The slot is empty or invalid, pull a new alien from available pool
42+
const nextAlien = available.shift();
43+
newActiveIds[i] = nextAlien ? nextAlien.id : null;
44+
changed = true;
45+
}
46+
}
47+
48+
if (changed) {
49+
setActiveIds(newActiveIds);
50+
}
51+
}, [preferences, matches, dismissedIds, activeIds]);
1252

1353
if (!preferences) return null;
1454

15-
// Filter aliens based on max distance
16-
const visibleAliens = mockAliens.filter(a => a.distanceAU <= preferences.maxDistanceAU);
17-
1855
const handleMatch = (alien: AlienProfile) => {
1956
addMatch(alien);
2057
setSelectedAlien(null);
2158
setMatchedAlien(alien);
2259
};
2360

61+
const handleDismiss = (alien: AlienProfile) => {
62+
setDismissedIds(prev => {
63+
const newSet = new Set(prev);
64+
newSet.add(alien.id);
65+
return newSet;
66+
});
67+
setSelectedAlien(null);
68+
};
69+
70+
const visibleAliens = mockAliens.filter(a => activeIds.includes(a.id));
71+
2472
return (
2573
<>
2674
<style>{`
2775
@keyframes orbit {
28-
from { transform: rotate(0deg) translateX(var(--radius)) rotate(0deg); }
29-
to { transform: rotate(360deg) translateX(var(--radius)) rotate(-360deg); }
76+
from { offset-distance: 0%; }
77+
to { offset-distance: 100%; }
3078
}
3179
.orbit-ring {
3280
position: absolute;
@@ -39,14 +87,14 @@ export default function OrbitSystem() {
3987
}
4088
.orbit-item {
4189
position: absolute;
42-
top: 50%;
43-
left: 50%;
44-
margin-top: -30px;
45-
margin-left: -30px;
4690
width: 60px;
4791
height: 60px;
92+
margin-top: -30px; /* Center relative to offset path */
93+
margin-left: -30px;
4894
border-radius: 50%;
4995
cursor: pointer;
96+
offset-path: ellipse(var(--rx) var(--ry) at 50% 50%);
97+
offset-rotate: 0deg;
5098
animation: orbit var(--duration) linear infinite;
5199
}
52100
.orbit-avatar {
@@ -69,7 +117,7 @@ export default function OrbitSystem() {
69117
}
70118
`}</style>
71119

72-
<div style={{ position: 'relative', width: '100%', maxWidth: '800px', height: '600px', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
120+
<div style={{ position: 'relative', width: '100%', maxWidth: '800px', height: '500px', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
73121

74122
{/* User Center */}
75123
<div style={{
@@ -90,27 +138,34 @@ export default function OrbitSystem() {
90138
</div>
91139

92140
{/* Orbit Rings and Aliens */}
93-
{visibleAliens.map((alien, i) => {
94-
// Calculate radius based on distance (min 80px, max 280px)
95-
const radiusRatio = preferences.maxDistanceAU > 0 ? alien.distanceAU / preferences.maxDistanceAU : 1;
96-
const radius = 80 + (radiusRatio * 200);
97-
const duration = 15 + (radius / 10); // Slower orbit for further objects
98-
const startAngle = (i * (360 / visibleAliens.length));
141+
{activeIds.map((id, i) => {
142+
if (!id) return null; // If no alien is available for this slot, don't render it
143+
144+
const alien = mockAliens.find(a => a.id === id);
145+
if (!alien) return null;
146+
147+
// Assign each slot to a distinct track (0 to 4)
148+
const rx = 120 + (i * 65);
149+
const ry = 80 + (i * 40);
150+
const duration = 15 + (i * 8);
151+
152+
const delay = -1 * (i * (duration / 5));
99153

100154
return (
101-
<div key={alien.id}>
155+
<div key={`track-${i}-${alien.id}`}>
102156
{/* Ring */}
103-
<div className="orbit-ring" style={{ width: `${radius * 2}px`, height: `${radius * 2}px` }} />
157+
<div className="orbit-ring" style={{ width: `${rx * 2}px`, height: `${ry * 2}px` }} />
104158

105159
{/* Profile */}
106160
<div
107161
className="orbit-item"
108162
onClick={() => setSelectedAlien(alien)}
109163
style={{
110164
// @ts-ignore
111-
'--radius': `${radius}px`,
165+
'--rx': `${rx}px`,
166+
'--ry': `${ry}px`,
112167
'--duration': `${duration}s`,
113-
animationDelay: `-${startAngle}s` // Stagger start positions
168+
animationDelay: `${delay}s`
114169
}}
115170
title={`${alien.name} (${alien.distanceAU} AU)`}
116171
>
@@ -126,6 +181,7 @@ export default function OrbitSystem() {
126181
alien={selectedAlien}
127182
onClose={() => setSelectedAlien(null)}
128183
onMatch={handleMatch}
184+
onDismiss={handleDismiss}
129185
/>
130186
)}
131187

src/components/ProfileModal.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ interface ProfileModalProps {
55
alien: AlienProfile;
66
onClose: () => void;
77
onMatch: (alien: AlienProfile) => void;
8+
onDismiss: (alien: AlienProfile) => void;
89
}
910

10-
export default function ProfileModal({ alien, onClose, onMatch }: ProfileModalProps) {
11+
export default function ProfileModal({ alien, onClose, onMatch, onDismiss }: ProfileModalProps) {
1112
return (
1213
<div style={{
1314
position: 'fixed',
@@ -119,7 +120,7 @@ export default function ProfileModal({ alien, onClose, onMatch }: ProfileModalPr
119120

120121
<div style={{ display: 'flex', justifyContent: 'space-around', marginTop: '24px' }}>
121122
<button
122-
onClick={onClose}
123+
onClick={() => onDismiss(alien)}
123124
style={{
124125
width: '60px', height: '60px', borderRadius: '50%',
125126
background: 'rgba(46, 41, 78, 0.8)', border: '2px solid var(--color-white)',

0 commit comments

Comments
 (0)