2025-02-22 01:16:33 -05:00
|
|
|
// components/sheets/NostrLoginSheet.tsx
|
|
|
|
import React, { useState } from 'react';
|
2025-03-03 22:40:11 -05:00
|
|
|
import { Modal, View, StyleSheet, Platform, KeyboardAvoidingView, ScrollView, ActivityIndicator, TouchableOpacity } from 'react-native';
|
|
|
|
import { Info, X } from 'lucide-react-native';
|
2025-02-22 01:16:33 -05:00
|
|
|
import { Text } from '@/components/ui/text';
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
import { Input } from '@/components/ui/input';
|
|
|
|
import { useNDKAuth } from '@/lib/hooks/useNDK';
|
|
|
|
|
|
|
|
interface NostrLoginSheetProps {
|
|
|
|
open: boolean;
|
|
|
|
onClose: () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function NostrLoginSheet({ open, onClose }: NostrLoginSheetProps) {
|
|
|
|
const [privateKey, setPrivateKey] = useState('');
|
2025-03-03 22:40:11 -05:00
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const { login, generateKeys, isLoading } = useNDKAuth();
|
2025-02-22 01:16:33 -05:00
|
|
|
|
2025-03-03 22:40:11 -05:00
|
|
|
// Handle key generation
|
|
|
|
const handleGenerateKeys = async () => {
|
|
|
|
try {
|
|
|
|
const { nsec } = generateKeys();
|
|
|
|
setPrivateKey(nsec);
|
|
|
|
setError(null);
|
|
|
|
} catch (err) {
|
|
|
|
setError('Failed to generate keys');
|
|
|
|
console.error('Key generation error:', err);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Handle login
|
2025-02-22 01:16:33 -05:00
|
|
|
const handleLogin = async () => {
|
|
|
|
if (!privateKey.trim()) {
|
2025-03-03 22:40:11 -05:00
|
|
|
setError('Please enter your private key or generate a new one');
|
2025-02-22 01:16:33 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-03-03 22:40:11 -05:00
|
|
|
setError(null);
|
2025-02-22 01:16:33 -05:00
|
|
|
try {
|
|
|
|
const success = await login(privateKey);
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
setPrivateKey('');
|
|
|
|
onClose();
|
|
|
|
} else {
|
2025-03-03 22:40:11 -05:00
|
|
|
setError('Failed to login with the provided key');
|
2025-02-22 01:16:33 -05:00
|
|
|
}
|
2025-03-03 22:40:11 -05:00
|
|
|
} catch (err) {
|
|
|
|
console.error('Login error:', err);
|
|
|
|
setError(err instanceof Error ? err.message : 'An unexpected error occurred');
|
2025-02-22 01:16:33 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-03-03 22:40:11 -05:00
|
|
|
if (!open) return null;
|
|
|
|
|
2025-02-22 01:16:33 -05:00
|
|
|
return (
|
2025-03-03 22:40:11 -05:00
|
|
|
<Modal
|
|
|
|
visible={open}
|
|
|
|
transparent={true}
|
|
|
|
animationType="slide"
|
|
|
|
onRequestClose={onClose}
|
2025-02-22 01:16:33 -05:00
|
|
|
>
|
2025-03-03 22:40:11 -05:00
|
|
|
<View style={styles.modalOverlay}>
|
|
|
|
<View style={styles.modalContent}>
|
|
|
|
<View style={styles.header}>
|
|
|
|
<Text style={styles.title}>Login with Nostr</Text>
|
|
|
|
<TouchableOpacity onPress={onClose}>
|
|
|
|
<X size={24} />
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
2025-02-22 01:16:33 -05:00
|
|
|
|
2025-03-03 22:40:11 -05:00
|
|
|
<KeyboardAvoidingView
|
|
|
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
|
|
style={styles.container}
|
|
|
|
>
|
|
|
|
<ScrollView style={styles.scrollView}>
|
|
|
|
<View style={styles.content}>
|
|
|
|
<Text className="mb-2 text-base">Enter your Nostr private key (nsec)</Text>
|
|
|
|
<Input
|
|
|
|
placeholder="nsec1..."
|
|
|
|
value={privateKey}
|
|
|
|
onChangeText={setPrivateKey}
|
|
|
|
secureTextEntry
|
|
|
|
autoCapitalize="none"
|
|
|
|
className="mb-4"
|
|
|
|
/>
|
|
|
|
|
|
|
|
{error && (
|
|
|
|
<View className="mb-4 p-3 bg-destructive/10 rounded-md border border-destructive">
|
|
|
|
<Text className="text-destructive">{error}</Text>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
|
|
|
|
<View className="flex-row space-x-2 mb-6">
|
|
|
|
<Button
|
|
|
|
variant="outline"
|
|
|
|
onPress={handleGenerateKeys}
|
|
|
|
disabled={isLoading}
|
|
|
|
className="flex-1"
|
|
|
|
>
|
|
|
|
<Text>Generate New Keys</Text>
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
<Button
|
|
|
|
onPress={handleLogin}
|
|
|
|
disabled={isLoading}
|
|
|
|
className="flex-1"
|
|
|
|
>
|
|
|
|
{isLoading ? (
|
|
|
|
<ActivityIndicator size="small" color="#fff" style={styles.loader} />
|
|
|
|
) : (
|
|
|
|
<Text>Login</Text>
|
|
|
|
)}
|
|
|
|
</Button>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<View className="bg-secondary/30 p-3 rounded-md">
|
|
|
|
<View className="flex-row items-center mb-2">
|
|
|
|
<Info size={16} className="mr-3 text-muted-foreground" />
|
|
|
|
<Text className="font-semibold">What is a Nostr Key?</Text>
|
|
|
|
</View>
|
|
|
|
<Text className="text-sm text-muted-foreground mb-2">
|
|
|
|
Nostr is a decentralized protocol where your private key (nsec) is your identity and password.
|
|
|
|
</Text>
|
|
|
|
<Text className="text-sm text-muted-foreground">
|
|
|
|
Your private key is securely stored on your device and is never sent to any servers.
|
|
|
|
</Text>
|
2025-02-22 01:16:33 -05:00
|
|
|
</View>
|
|
|
|
</View>
|
2025-03-03 22:40:11 -05:00
|
|
|
</ScrollView>
|
|
|
|
</KeyboardAvoidingView>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</Modal>
|
2025-02-22 01:16:33 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
2025-03-03 22:40:11 -05:00
|
|
|
modalOverlay: {
|
2025-02-22 01:16:33 -05:00
|
|
|
flex: 1,
|
2025-03-03 22:40:11 -05:00
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
|
|
},
|
|
|
|
modalContent: {
|
|
|
|
width: '90%',
|
|
|
|
maxWidth: 500,
|
|
|
|
backgroundColor: '#FFFFFF',
|
|
|
|
borderRadius: 12,
|
|
|
|
padding: 20,
|
|
|
|
shadowColor: '#000',
|
|
|
|
shadowOffset: { width: 0, height: 2 },
|
|
|
|
shadowOpacity: 0.25,
|
|
|
|
shadowRadius: 3.84,
|
|
|
|
elevation: 5,
|
|
|
|
},
|
|
|
|
header: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
alignItems: 'center',
|
|
|
|
marginBottom: 15,
|
|
|
|
},
|
|
|
|
title: {
|
|
|
|
fontSize: 18,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
},
|
|
|
|
container: {
|
|
|
|
maxHeight: '80%',
|
2025-02-22 01:16:33 -05:00
|
|
|
},
|
|
|
|
scrollView: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
content: {
|
2025-03-03 22:40:11 -05:00
|
|
|
padding: 10,
|
2025-02-22 01:16:33 -05:00
|
|
|
},
|
2025-03-03 22:40:11 -05:00
|
|
|
loader: {
|
|
|
|
marginRight: 8,
|
|
|
|
}
|
2025-02-22 01:16:33 -05:00
|
|
|
});
|