import { useState, useCallback, useEffect } from 'react';
import {
View, Text, StyleSheet, Pressable, ActivityIndicator, Dimensions,
} from 'react-native';
import Animated, {
useSharedValue, useAnimatedStyle, withSpring, withTiming,
FadeIn, SlideInDown,
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
import * as WebBrowser from 'expo-web-browser';
import * as Linking from 'expo-linking';
import { router } from 'expo-router';
import Svg, { Path, Circle, Polyline, Line } from 'react-native-svg';
import { Colors, Font, Radius, Spacing } from '../constants/theme';
import { buildOAuthUrl, exchangeCode, saveTokens } from '../services/api';
import { useAuth } from '../store';
WebBrowser.maybeCompleteAuthSession();
const { width, height } = Dimensions.get('window');
export default function AuthScreen() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const { setUser } = useAuth();
const btnScale = useSharedValue(1);
const btnStyle = useAnimatedStyle(() => ({
transform: [{ scale: btnScale.value }],
}));
const handleLogin = useCallback(async () => {
setLoading(true);
setError('');
try {
const state = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
const url = buildOAuthUrl(state);
const result = await WebBrowser.openAuthSessionAsync(url, 'burmalda://auth/callback');
if (result.type !== 'success') {
setError('Авторизация отменена');
return;
}
const parsed = Linking.parse(result.url);
const code = parsed.queryParams?.code as string | undefined;
if (!code) {
setError((parsed.queryParams?.error as string) || 'Ошибка авторизации');
return;
}
const data = await exchangeCode(code);
await saveTokens(data.access_token, data.refresh_token);
setUser(data.user);
router.replace('/(tabs)');
} catch (e: any) {
setError(e.message || 'Что-то пошло не так');
} finally {
setLoading(false);
}
}, [setUser]);
return (
Burmalda Music
by NodeShift
{ btnScale.value = withSpring(0.96, { damping: 14 }); }}
onPressOut={() => { btnScale.value = withSpring(1, { damping: 14 }); }}
onPress={handleLogin}
>
{loading
?
: (
)}
{loading ? 'Ожидаем авторизацию…' : 'Войти через NodeShift'}
{!!error && (
{error}
)}
Нажимая «Войти», вы соглашаетесь с{'\n'}условиями использования NodeShift
);
}
function Blob({ color, size, left, top, delay }: {
color: string; size: number; left: number; top: number; delay: number;
}) {
const tx = useSharedValue(0);
const ty = useSharedValue(0);
useEffect(() => {
const animate = () => {
tx.value = withTiming(25, { duration: 8000 }, () =>
tx.value = withTiming(-20, { duration: 8000 }, () =>
tx.value = withTiming(0, { duration: 8000 }, animate)));
ty.value = withTiming(-35, { duration: 8000 }, () =>
ty.value = withTiming(18, { duration: 8000 }, () =>
ty.value = withTiming(0, { duration: 8000 })));
};
const t = setTimeout(animate, delay);
return () => clearTimeout(t);
}, []);
const style = useAnimatedStyle(() => ({
transform: [{ translateX: tx.value }, { translateY: ty.value }],
}));
return (
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: Colors.bg, alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },
blob: { position: 'absolute', opacity: 0.15 },
card: { width: '100%', maxWidth: 360, alignItems: 'center', paddingHorizontal: Spacing.xl, zIndex: 10 },
logoWrap: { alignItems: 'center', marginBottom: 40 },
logoIcon: { width: 84, height: 84, borderRadius: 22, alignItems: 'center', justifyContent: 'center', marginBottom: Spacing.md, shadowColor: '#1db954', shadowOffset: { width: 0, height: 14 }, shadowOpacity: 0.45, shadowRadius: 22 },
logoTitle: { color: Colors.text, fontSize: Font.xxl, fontWeight: '800', letterSpacing: -0.5, marginBottom: 4 },
logoSub: { color: Colors.text3, fontSize: Font.sm, fontWeight: '500' },
btnWrap: { width: '100%', borderRadius: Radius.md, overflow: 'hidden', shadowColor: '#1db954', shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.35, shadowRadius: 18 },
btn: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10, paddingVertical: 16, paddingHorizontal: 20 },
btnText: { color: '#000', fontSize: Font.md, fontWeight: '700' },
error: { color: Colors.red, fontSize: Font.sm, textAlign: 'center', marginTop: Spacing.sm },
footer: { marginTop: Spacing.xl, color: Colors.text3, fontSize: Font.xs, textAlign: 'center', lineHeight: 18 },
});