Skip to content

Commit 980ce76

Browse files
committed
feat: Implement useReportNotifications hook with robust error handling and direct API calls for better reliability when fetching report notifications, marking notifications as read, and handling reported event actions
1 parent 495630c commit 980ce76

1 file changed

Lines changed: 250 additions & 0 deletions

File tree

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
import { useState, useEffect, useCallback } from 'react';
2+
import { notificationController } from '@app/controllers/notificationController';
3+
import config from '@app/config/config';
4+
import { readToken } from '@app/services/localStorage.service';
5+
import { useHandleLogout } from './authUtils';
6+
7+
export interface ReportNotificationParams {
8+
page?: number;
9+
limit?: number;
10+
filter?: 'all' | 'unread';
11+
}
12+
13+
export interface ReportNotification {
14+
id: number;
15+
pubkey: string;
16+
event_id: string;
17+
report_type: string;
18+
report_content: string;
19+
reporter_pubkey: string;
20+
report_count: number;
21+
created_at: string;
22+
updated_at: string;
23+
is_read: boolean;
24+
}
25+
26+
export interface PaginationData {
27+
currentPage: number;
28+
pageSize: number;
29+
totalItems: number;
30+
totalPages: number;
31+
hasNext: boolean;
32+
hasPrevious: boolean;
33+
}
34+
35+
export interface UseReportNotificationsReturn {
36+
notifications: ReportNotification[];
37+
pagination: PaginationData | null;
38+
isLoading: boolean;
39+
error: string | null;
40+
fetchNotifications: (params?: ReportNotificationParams) => Promise<void>;
41+
markAsRead: (id: number) => Promise<void>;
42+
markAllAsRead: () => Promise<void>;
43+
getReportedEventContent: (eventId: string) => Promise<{ event: any } | null>;
44+
deleteReportedEvent: (eventId: string) => Promise<boolean>;
45+
}
46+
47+
export const useReportNotifications = (): UseReportNotificationsReturn => {
48+
const [notifications, setNotifications] = useState<ReportNotification[]>([]);
49+
const [pagination, setPagination] = useState<PaginationData | null>(null);
50+
const [isLoading, setIsLoading] = useState<boolean>(true);
51+
const [error, setError] = useState<string | null>(null);
52+
53+
const handleLogout = useHandleLogout();
54+
const token = readToken();
55+
56+
const fetchNotifications = useCallback(async (params: ReportNotificationParams = {}) => {
57+
setIsLoading(true);
58+
setError(null);
59+
60+
try {
61+
// Construct query parameters
62+
const queryParams = new URLSearchParams();
63+
if (params.page) queryParams.append('page', params.page.toString());
64+
if (params.limit) queryParams.append('limit', params.limit.toString());
65+
if (params.filter) queryParams.append('filter', params.filter);
66+
67+
const response = await fetch(`${config.baseURL}/api/reports/notifications?${queryParams}`, {
68+
headers: {
69+
'Content-Type': 'application/json',
70+
'Authorization': `Bearer ${token}`,
71+
},
72+
});
73+
74+
if (response.status === 204) {
75+
// 204 No Content means no notifications, return empty arrays
76+
setNotifications([]);
77+
setPagination({
78+
currentPage: 1,
79+
pageSize: params.limit || 10,
80+
totalItems: 0,
81+
totalPages: 0,
82+
hasNext: false,
83+
hasPrevious: false
84+
});
85+
return;
86+
} else if (!response.ok) {
87+
if (response.status === 401) {
88+
handleLogout();
89+
return;
90+
}
91+
throw new Error(`Request failed: ${response.status}`);
92+
}
93+
94+
// Parse JSON if there's content
95+
const data = await response.json();
96+
97+
setNotifications(data.notifications);
98+
setPagination(data.pagination);
99+
} catch (err) {
100+
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch report notifications';
101+
setError(errorMessage);
102+
console.error('Failed to fetch report notifications:', err);
103+
} finally {
104+
setIsLoading(false);
105+
}
106+
}, [token, handleLogout]);
107+
108+
// Initial fetch on mount
109+
useEffect(() => {
110+
fetchNotifications();
111+
}, [fetchNotifications]);
112+
113+
const markAsRead = useCallback(async (id: number) => {
114+
try {
115+
const response = await fetch(`${config.baseURL}/api/reports/notifications/read`, {
116+
method: 'POST',
117+
headers: {
118+
'Content-Type': 'application/json',
119+
'Authorization': `Bearer ${token}`,
120+
},
121+
body: JSON.stringify({ id }),
122+
});
123+
124+
if (!response.ok) {
125+
if (response.status === 401) {
126+
handleLogout();
127+
return;
128+
}
129+
throw new Error(`Request failed: ${response.status}`);
130+
}
131+
132+
// Update local state
133+
setNotifications((prevNotifications) =>
134+
prevNotifications.map((notification) =>
135+
notification.id === id
136+
? { ...notification, is_read: true }
137+
: notification
138+
)
139+
);
140+
} catch (err) {
141+
const errorMessage = err instanceof Error ? err.message : 'Failed to mark notification as read';
142+
notificationController.error({ message: errorMessage });
143+
console.error('Failed to mark notification as read:', err);
144+
}
145+
}, [token, handleLogout]);
146+
147+
const markAllAsRead = useCallback(async () => {
148+
try {
149+
const response = await fetch(`${config.baseURL}/api/reports/notifications/read-all`, {
150+
method: 'POST',
151+
headers: {
152+
'Content-Type': 'application/json',
153+
'Authorization': `Bearer ${token}`,
154+
},
155+
});
156+
157+
if (!response.ok) {
158+
if (response.status === 401) {
159+
handleLogout();
160+
return;
161+
}
162+
throw new Error(`Request failed: ${response.status}`);
163+
}
164+
165+
// Update local state
166+
setNotifications((prevNotifications) =>
167+
prevNotifications.map((notification) => ({
168+
...notification,
169+
is_read: true,
170+
}))
171+
);
172+
} catch (err) {
173+
const errorMessage = err instanceof Error ? err.message : 'Failed to mark all notifications as read';
174+
notificationController.error({ message: errorMessage });
175+
console.error('Failed to mark all notifications as read:', err);
176+
}
177+
}, [token, handleLogout]);
178+
179+
const getReportedEventContent = useCallback(async (eventId: string) => {
180+
try {
181+
const response = await fetch(`${config.baseURL}/api/reports/event/${eventId}`, {
182+
headers: {
183+
'Content-Type': 'application/json',
184+
'Authorization': `Bearer ${token}`,
185+
},
186+
});
187+
188+
if (!response.ok) {
189+
if (response.status === 401) {
190+
handleLogout();
191+
return null;
192+
}
193+
throw new Error(`Request failed: ${response.status}`);
194+
}
195+
196+
const data = await response.json();
197+
return data;
198+
} catch (err) {
199+
console.error('Failed to fetch reported event content:', err);
200+
return null;
201+
}
202+
}, [token, handleLogout]);
203+
204+
const deleteReportedEvent = useCallback(async (eventId: string) => {
205+
try {
206+
const response = await fetch(`${config.baseURL}/api/reports/event/${eventId}`, {
207+
method: 'DELETE',
208+
headers: {
209+
'Content-Type': 'application/json',
210+
'Authorization': `Bearer ${token}`,
211+
},
212+
});
213+
214+
if (!response.ok) {
215+
if (response.status === 401) {
216+
handleLogout();
217+
return false;
218+
}
219+
throw new Error(`Request failed: ${response.status}`);
220+
}
221+
222+
const result = await response.json();
223+
224+
if (result.success) {
225+
// Remove the deleted notification from state
226+
setNotifications((prevNotifications) =>
227+
prevNotifications.filter((notification) => notification.event_id !== eventId)
228+
);
229+
return true;
230+
}
231+
232+
return false;
233+
} catch (err) {
234+
console.error('Failed to delete reported event:', err);
235+
return false;
236+
}
237+
}, [token, handleLogout]);
238+
239+
return {
240+
notifications,
241+
pagination,
242+
isLoading,
243+
error,
244+
fetchNotifications,
245+
markAsRead,
246+
markAllAsRead,
247+
getReportedEventContent,
248+
deleteReportedEvent,
249+
};
250+
};

0 commit comments

Comments
 (0)