Pierwszy commit

This commit is contained in:
2024-09-10 21:30:40 +02:00
commit a616741470
31 changed files with 1070 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { useAtom } from 'jotai';
import { authAtom, updateAuthAtom } from '../atoms/authAtom';
import { pb } from '../lib/pocketbase';
const Header: React.FC = () => {
const [isAuth] = useAtom(authAtom);
const [, updateAuth] = useAtom(updateAuthAtom);
const handleLogout = () => {
pb.authStore.clear();
updateAuth();
};
return (
<header className="bg-blue-500 p-4">
<div className="container mx-auto flex justify-between items-center">
<Link to="/" className="text-white text-2xl font-bold">Reddit-Like-App</Link>
<Link to="https://gitea.garandplg.com/GarandPLG/reddit-like-app" className="text-white text-xl font-bold">Kod źródłowy</Link>
<nav>
{isAuth ? (
<>
<Link to="/create-post" className="text-white mr-4">Stwórz post</Link>
<Link to="/profile" className="text-white mr-4">Profil</Link>
<button onClick={handleLogout} className="text-white">Wyloguj się</button>
</>
) : (
<>
<Link to="/login" className="text-white mr-4">Zaloguj się</Link>
<Link to="/register" className="text-white">Zarejestruj się</Link>
</>
)}
</nav>
</div>
</header>
);
};
export default Header;
// komponent odpowiedzialny za nagłówek strony
+103
View File
@@ -0,0 +1,103 @@
import React, { useState, useEffect } from 'react';
import { useAtom } from 'jotai';
import { authAtom } from '../atoms/authAtom';
import { pb } from '../lib/pocketbase';
interface PostProps {
post: {
id: string;
title: string;
content: string;
user: string;
votes: number;
created: string;
};
onVoteChange?: (postId: string, newVotes: number) => void;
}
const Post: React.FC<PostProps> = ({ post, onVoteChange }) => {
const [isAuth] = useAtom(authAtom);
const [username, setUsername] = useState<string>('');
const [votes, setVotes] = useState(post.votes);
useEffect(() => {
const fetchUsername = async () => {
try {
const user = await pb.collection("users").getOne(post.user);
setUsername(user.username);
} catch (error) {
console.error('Błąd przy pobieraniu nazwy użytkownika:', error);
setUsername('Nieznany użytkownik');
}
};
fetchUsername();
}, [post.user]);
useEffect(() => {
const subscribeToPost = async () => {
try {
pb.realtime.subscribe(`posts/${post.id}`, (e) => {
if (e.action === 'update' && e.record.id === post.id) {
setVotes(e.record.votes);
}
});
} catch (error) {
console.error('Błąd przy renderowaniu głosów in real time:', error);
}
};
subscribeToPost();
return () => {
pb.realtime.unsubscribe(`posts/${post.id}`);
};
}, [post.id]);
const handleVote = async (type: 'up' | 'down') => {
if (!isAuth) {
alert('Musisz się zalogować, by móc głosować.');
return;
}
const newVotes = type === 'up' ? votes + 1 : votes - 1;
setVotes(newVotes);
try {
await pb.collection('posts').update(post.id, { votes: newVotes });
if (onVoteChange) {
onVoteChange(post.id, newVotes);
}
} catch (error) {
console.error('Błąd przy głosowaniu:', error);
setVotes(votes);
alert('Nie udało się dodać głosu. Spróbuj jeszcze raz.');
}
};
return (
<div className="bg-white p-4 rounded mt-2">
<div className='mb-4'>
<span className="mt-4 mr-4">
<button onClick={() => handleVote('up')} className="mr-2">👍</button>
<span>{votes}</span>
<button onClick={() => handleVote('down')} className="ml-2">👎</button>
</span>
<span className='text-xl'>|</span>
<span className="mb-4 mx-4 text-gray-500 text-lg">
{username}
</span>
<span className='text-xl'>|</span>
<span className="my-2 ml-4">{new Date(post.created).toLocaleString()}</span>
</div>
<h3 className="text-2xl font-bold">{post.title}</h3>
<p className="mt-2">{post.content}</p>
</div>
)};
export default Post;
// Komponent odpowiedzialny za renderowanie pojedynczego posta.