Encrypted-Chat-Client/app/chat/[contactid].tsx

196 lines
5.8 KiB
TypeScript
Raw Normal View History

2024-12-15 14:40:35 +00:00
import React, { useEffect, useState } from 'react';
import { useLocalSearchParams } from 'expo-router';
import { YStack, XStack, Text, Input, Button, ScrollView } from 'tamagui';
import { chacha20Encrypt,chacha20Decrypt } from '../../components/crypto';
const ChatApp = () => {
const {friendUsername } = useLocalSearchParams(); // Friend's username passed via params
const [messages, setMessages] = useState([]);
const [inputText, setInputText] = useState('');
const [loading, setLoading] = useState(true);
const [friendPubKey, setFriendPubKey] = useState(null); // Friend's public key
// Fetch friend's public key
const fetchPublicKey = async () => {
try {
const authToken = localStorage.getItem('jwtToken');
if (!authToken) {
console.error('User is not authenticated');
return;
}
const response = await fetch(`http://localhost:4000/user/pubkey?username=${friendUsername}`, {
headers: {
Authorization: `Bearer ${authToken}`,
},
});
if (!response.ok) throw new Error('Failed to fetch public key');
const { pubKey } = await response.json();
setFriendPubKey(pubKey);
} catch (err) {
console.error('Error fetching public key:', err);
}
};
// Fetch messages
// Fetch messages
const fetchMessages = async () => {
try {
const authToken = localStorage.getItem('jwtToken');
if (!authToken) {
console.error('User is not authenticated');
return;
}
const response = await fetch(`http://localhost:4000/messages/?receiverUsername=${friendUsername}`, {
headers: {
Authorization: `Bearer ${authToken}`,
},
});
if (!response.ok) throw new Error('Failed to fetch messages');
// Fetch encrypted messages as JSON
const encryptedMessages = await response.json();
console.log('Encrypted Messages:', encryptedMessages); // Debug: log encrypted messages
// Decrypt each message and store the decrypted result
const decryptedMessages = await Promise.all(
encryptedMessages.map(async (msg) => {
const decryptedMessage = await chacha20Decrypt(msg);
return { ...msg, content: decryptedMessage }; // Attach decrypted message to original message
})
);
console.log('Decrypted Messages:', decryptedMessages); // Debug: log decrypted messages
// Update the state with the decrypted messages
setMessages(decryptedMessages);
} catch (err) {
console.error('Error fetching messages:', err);
setMessages(["Error fetching messages"]); // Provide a default error message
} finally {
setLoading(false);
}
};
// Fetch initial data
useEffect(() => {
fetchPublicKey();
fetchMessages();
// Poll for new messages every 5 seconds
const intervalId = setInterval(fetchMessages, 1000);
return () => clearInterval(intervalId); // Cleanup on component unmount
}, [friendUsername]);
// Send message
const sendMessage = async () => {
if (!inputText.trim() || !friendPubKey) return;
try {
const authToken = localStorage.getItem('jwtToken');
if (!authToken) {
console.error('User is not authenticated');
return;
}
// Prepare the JSON object for encryption
const messageData = JSON.stringify({
message: inputText,
receiver: friendUsername,
pubkey: friendPubKey,
});
// Encrypt the message using chacha20Encrypt
const encryptedPayload = chacha20Encrypt(messageData);
console.log("payload", JSON.parse(encryptedPayload))
const decrypted = chacha20Decrypt(encryptedPayload)
console.log("decrypted:", decrypted)
// Send the encrypted message to the server
const response = await fetch('http://localhost:4000/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${authToken}`,
},
body: encryptedPayload, // Send the encrypted payload
});
if (!response.ok) throw new Error('Failed to send message');
const newMessage = await response.json();
setMessages((prevMessages) => [...prevMessages, newMessage]);
setInputText('');
} catch (err) {
console.error('Error sending message:', err);
}
};
return (
<YStack flex={1} bg="$background" padding={10}>
{/* Chat Header */}
<Text fontSize={20} fontWeight="bold" mb={10}>
Chat with {friendUsername}
</Text>
{/* Messages List */}
<ScrollView flex={1} mb={10}>
{loading ? (
<Text>Loading...</Text>
) : (
messages.map((msg) => (
<MessageBubble
key={msg.id}
text={msg.content}
sender={msg.senderUsername === friendUsername ? 'friend' : 'user'}
/>
))
)}
</ScrollView>
{/* Input Field and Send Button */}
<XStack space={10} alignItems="center">
<Input
flex={1}
value={inputText}
onChangeText={setInputText}
placeholder="Type a message..."
onSubmitEditing={sendMessage}
/>
<Button size="large" onPress={sendMessage}>
Send
</Button>
</XStack>
</YStack>
);
};
const MessageBubble = ({ text, sender }) => {
const isUser = sender === 'user';
return (
<XStack
justifyContent={isUser ? 'flex-end' : 'flex-start'}
paddingVertical={5}
paddingHorizontal={10}
>
<YStack
maxWidth="70%"
padding={10}
borderRadius={10}
backgroundColor={isUser ? '#007aff' : '#e5e5ea'}
>
<Text color={isUser ? '#fff' : '#000'}>{text}</Text>
</YStack>
</XStack>
);
};
export default ChatApp;