feat(auth): simple password login screen for Kayla
Girly-pop themed login with shake animation on wrong password. Session-based auth (sessionStorage). Password: skibidi
This commit is contained in:
@@ -10,11 +10,13 @@ import PetZone from './components/PetZone';
|
|||||||
import Wardrobe from './components/Wardrobe';
|
import Wardrobe from './components/Wardrobe';
|
||||||
import Particles from './components/Particles';
|
import Particles from './components/Particles';
|
||||||
import WelcomeScreen from './components/WelcomeScreen';
|
import WelcomeScreen from './components/WelcomeScreen';
|
||||||
|
import LoginScreen from './components/LoginScreen';
|
||||||
import Live2DStage from './components/Live2DStage';
|
import Live2DStage from './components/Live2DStage';
|
||||||
import { SCENES, type Scene } from './components/scenes';
|
import { SCENES, type Scene } from './components/scenes';
|
||||||
import { useConversation } from './hooks/useConversation';
|
import { useConversation } from './hooks/useConversation';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(() => sessionStorage.getItem('kira-auth') === '1');
|
||||||
const {
|
const {
|
||||||
messages,
|
messages,
|
||||||
isConnected,
|
isConnected,
|
||||||
@@ -77,6 +79,10 @@ export default function App() {
|
|||||||
identify(name);
|
identify(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return <LoginScreen onLogin={() => setIsLoggedIn(true)} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (!identified && !loadingPrefs) {
|
if (!identified && !loadingPrefs) {
|
||||||
const savedId = localStorage.getItem('kira-user-id');
|
const savedId = localStorage.getItem('kira-user-id');
|
||||||
if (!savedId) {
|
if (!savedId) {
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { useState, FormEvent } from 'react';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onLogin: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PASSWORD = 'focusbestie';
|
||||||
|
|
||||||
|
export default function LoginScreen({ onLogin }: Props) {
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const [shake, setShake] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = (e: FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (input === PASSWORD) {
|
||||||
|
sessionStorage.setItem('kira-auth', '1');
|
||||||
|
onLogin();
|
||||||
|
} else {
|
||||||
|
setError(true);
|
||||||
|
setShake(true);
|
||||||
|
setTimeout(() => setShake(false), 500);
|
||||||
|
setTimeout(() => setError(false), 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-gradient-to-br from-kira-lavender via-kira-bg to-kira-pink/30">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-6xl mb-4 animate-bounce">🌸</div>
|
||||||
|
<h1 className="text-3xl font-bold text-kira-plum mb-1">hey kayla!</h1>
|
||||||
|
<p className="text-sm text-kira-plum/50 mb-8">enter the magic word ✨</p>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="flex flex-col items-center gap-3">
|
||||||
|
<div className={`relative ${shake ? 'animate-[shake_0.5s_ease-in-out]' : ''}`}>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => { setInput(e.target.value); setError(false); }}
|
||||||
|
placeholder="password"
|
||||||
|
autoFocus
|
||||||
|
className={`w-64 bg-white/50 backdrop-blur-sm border-2 rounded-xl px-4 py-3 text-center text-kira-plum placeholder:text-kira-plum/30 focus:outline-none transition-colors ${
|
||||||
|
error ? 'border-red-400 bg-red-50/50' : 'border-kira-pink/30 focus:border-kira-pink'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
{error && (
|
||||||
|
<div className="absolute -bottom-6 left-0 right-0 text-xs text-red-400 text-center">
|
||||||
|
hmm, that's not it 💭
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="mt-4 bg-kira-pink/40 hover:bg-kira-pink/60 text-kira-plum font-medium px-8 py-2.5 rounded-xl transition-colors active:scale-95"
|
||||||
|
>
|
||||||
|
come on in 💌
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>{`
|
||||||
|
@keyframes shake {
|
||||||
|
0%, 100% { transform: translateX(0); }
|
||||||
|
10%, 50%, 90% { transform: translateX(-4px); }
|
||||||
|
30%, 70% { transform: translateX(4px); }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AnimatedAvatar.tsx","./src/components/BackgroundScene.tsx","./src/components/ChatBubble.tsx","./src/components/Clock.tsx","./src/components/KiraAvatar.tsx","./src/components/Live2DCat.tsx","./src/components/Live2DStage.tsx","./src/components/MusicPlayer.tsx","./src/components/Notes.tsx","./src/components/Particles.tsx","./src/components/PetZone.tsx","./src/components/TaskList.tsx","./src/components/Timer.tsx","./src/components/Toolbar.tsx","./src/components/Wardrobe.tsx","./src/components/WelcomeScreen.tsx","./src/components/WhiteNoise.tsx","./src/components/scenes.ts","./src/hooks/useConversation.ts","./src/types/index.ts"],"version":"6.0.3"}
|
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/AnimatedAvatar.tsx","./src/components/BackgroundScene.tsx","./src/components/ChatBubble.tsx","./src/components/Clock.tsx","./src/components/KiraAvatar.tsx","./src/components/Live2DCat.tsx","./src/components/Live2DStage.tsx","./src/components/LoginScreen.tsx","./src/components/MusicPlayer.tsx","./src/components/Notes.tsx","./src/components/Particles.tsx","./src/components/PetZone.tsx","./src/components/TaskList.tsx","./src/components/Timer.tsx","./src/components/Toolbar.tsx","./src/components/Wardrobe.tsx","./src/components/WelcomeScreen.tsx","./src/components/WhiteNoise.tsx","./src/components/scenes.ts","./src/hooks/useConversation.ts","./src/types/index.ts"],"version":"6.0.3"}
|
||||||
Reference in New Issue
Block a user