diff --git a/client/app/app/notifications/layout.jsx b/client/app/app/notifications/layout.jsx
new file mode 100644
index 0000000..275b382
--- /dev/null
+++ b/client/app/app/notifications/layout.jsx
@@ -0,0 +1,7 @@
+export const metadata = {
+ title: 'bildirimler'
+};
+
+export default function Layout({ children }) {
+ return children;
+}
diff --git a/client/app/app/notifications/page.jsx b/client/app/app/notifications/page.jsx
index e095606..e94549a 100644
--- a/client/app/app/notifications/page.jsx
+++ b/client/app/app/notifications/page.jsx
@@ -1,7 +1,111 @@
-export const metadata = {
- title: 'bildirimler'
-};
+'use client';
+
+import { useState, useEffect } from 'react';
+import { Button } from '@/components/ui/button';
+import { useToast } from '@/components/ui/use-toast';
+import Notification from '@/components/app/Notification';
+import NotificationSkeleton from '@/components/app/Notification/Skeleton';
+import { getNotifications } from '@/lib/api/me';
export default function Page() {
- return
bu sayfa henüz hazır değil {':('}
;
+ const [notifications, setNotifications] = useState([]);
+ const [offset, setOffset] = useState(10);
+ const [hasMoreNotification, setHasMoreNotification] = useState(true);
+ const [loading, setLoading] = useState(false);
+ const { toast } = useToast();
+
+ const loadMoreNotifications = async () => {
+ if (!hasMoreNotification) return;
+
+ const response = await getNotifications(11, offset);
+ if (!response || response.status === 429) {
+ toast({
+ title: 'hay aksi, bir şeyler ters gitti!',
+ description:
+ 'sunucudan yanıt alınamadı. lütfen daha sonra tekrar deneyin.',
+ duration: 3000
+ });
+ return;
+ }
+
+ const newNotifications = response.data.notifications || [];
+
+ if (newNotifications.length > 10) {
+ setNotifications((prevNotifications) => [
+ ...prevNotifications,
+ ...newNotifications.slice(0, 10)
+ ]);
+ } else {
+ setNotifications((prevNotifications) => [
+ ...prevNotifications,
+ ...newNotifications
+ ]);
+ setHasMoreNotification(false);
+ }
+
+ setOffset((prevOffset) => prevOffset + 10);
+ };
+
+ useEffect(
+ () => {
+ const fetchInitialNotifications = async () => {
+ setLoading(true);
+ const response = await getNotifications(11, 0);
+
+ if (!response || response.status === 429) {
+ toast({
+ title: 'hay aksi, bir şeyler ters gitti!',
+ description:
+ 'sunucudan yanıt alınamadı. Lütfen daha sonra tekrar deneyin.',
+ duration: 3000
+ });
+ return;
+ }
+
+ const initialNotifications = response.data.notifications || [];
+
+ if (initialNotifications.length > 10) {
+ setNotifications(initialNotifications.slice(0, 10));
+ } else {
+ setNotifications(initialNotifications);
+ setHasMoreNotification(false);
+ }
+
+ setLoading(false);
+ };
+
+ fetchInitialNotifications();
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ );
+
+ return (
+
+ {loading && (
+ <>
+
+
+ >
+ )}
+ {!loading && notifications.length > 0 ? (
+ <>
+ {notifications.map((notification) => (
+
+ ))}
+ {hasMoreNotification && (
+
+ )}
+ >
+ ) : (
+ !loading && (
+
+ buralar şimdilik sessiz.
+
+ )
+ )}
+
+ );
}
diff --git a/client/components/app/Navbar/Item.jsx b/client/components/app/Navbar/Item.jsx
index 9eeddf6..d9c4ba6 100644
--- a/client/components/app/Navbar/Item.jsx
+++ b/client/components/app/Navbar/Item.jsx
@@ -1,19 +1,46 @@
import Link from 'next/link';
+import { useState, useEffect } from 'react';
+import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
+import { getUnreadNotificationsCount } from '@/lib/api/me';
export default function Item({ pathname, href, icon: Icon, label }) {
const isActive =
pathname === href || (href !== '/app' && pathname.startsWith(href));
+ const [unreadNotificationsCount, setUnreadNotificationsCount] = useState(0);
+
+ useEffect(
+ () => {
+ const fetchUnreadNotificationsCount = async () => {
+ const response = await getUnreadNotificationsCount();
+ if (response) {
+ setUnreadNotificationsCount(response.data.unreads);
+ }
+ };
+
+ fetchUnreadNotificationsCount();
+ },
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
+ );
+
return (
);
diff --git a/client/components/app/Notification/Skeleton.jsx b/client/components/app/Notification/Skeleton.jsx
new file mode 100644
index 0000000..b71b13c
--- /dev/null
+++ b/client/components/app/Notification/Skeleton.jsx
@@ -0,0 +1,16 @@
+import { Skeleton } from '@/components/ui/skeleton';
+
+export default function NotificationSkeleton() {
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/client/components/app/Notification/index.jsx b/client/components/app/Notification/index.jsx
new file mode 100644
index 0000000..c9d759d
--- /dev/null
+++ b/client/components/app/Notification/index.jsx
@@ -0,0 +1,88 @@
+import Link from 'next/link';
+import { useState } from 'react';
+import { Mail, MailOpen, SquareArrowOutUpRight } from 'lucide-react';
+import UserInfo from '@/components/app/User/Info';
+import { Button } from '@/components/ui/button';
+import { useToast } from '@/components/ui/use-toast';
+import { updateNotification } from '@/lib/api/me';
+
+function typeContent(notification) {
+ switch (notification.type) {
+ case 'user_followed':
+ return {
+ href: `/app/users/${notification.type_content}`,
+ message: 'seni takip etmeye başladı.'
+ };
+ case 'post_liked':
+ return {
+ href: `/app/posts/${notification.type_content}`,
+ message: 'gönderini beğendi.'
+ };
+ case 'comment_created':
+ return {
+ href: `/app/posts/${notification.type_content}`,
+ message: 'gönderine yorum yaptı.'
+ };
+ case 'comment_liked':
+ return {
+ href: `/app/posts/${notification.type_content}`,
+ message: 'yorumunu beğendi.'
+ };
+ }
+}
+
+export default function Notification({ notification }) {
+ const [isRead, setIsRead] = useState(notification.read);
+ const { toast } = useToast();
+
+ const markAsRead = async ({ read }) => {
+ const response = await updateNotification({
+ id: notification.id,
+ read: !read
+ });
+ if (!response || response.status === 429) {
+ toast({
+ title: 'hay aksi, bir şeyler ters gitti!',
+ description:
+ 'sunucudan yanıt alınamadı. lütfen daha sonra tekrar deneyin.',
+ duration: 3000
+ });
+ return;
+ }
+
+ setIsRead(!read);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {notification.source_user.display_name} (
+ {notification.source_user.username})
+ {' '}
+ {typeContent(notification).message}
+
+
+ );
+}
diff --git a/client/lib/api/me/index.js b/client/lib/api/me/index.js
index 11c2167..3005965 100644
--- a/client/lib/api/me/index.js
+++ b/client/lib/api/me/index.js
@@ -25,4 +25,33 @@ export async function getFeed(limit, offset) {
} catch (error) {
return error.response;
}
-}
\ No newline at end of file
+}
+
+export async function getNotifications(limit, offset) {
+ try {
+ const response = await api.get('/me/notifications', {
+ params: { limit, offset }
+ });
+ return response;
+ } catch (error) {
+ return error.response;
+ }
+}
+
+export async function getUnreadNotificationsCount() {
+ try {
+ const response = await api.get('/me/notifications/unread');
+ return response;
+ } catch (error) {
+ return error.response;
+ }
+}
+
+export async function updateNotification({ id, read }) {
+ try {
+ const response = await api.patch(`/me/notifications/${id}`, { read });
+ return response;
+ } catch (error) {
+ return error.response;
+ }
+}