Improve error handling, automatically renew access token

This commit is contained in:
2025-11-06 20:47:08 +02:00
parent bb3900d23b
commit 65794be998
7 changed files with 374 additions and 175 deletions

View File

@@ -10,7 +10,7 @@ import type { Post } from "~/types/post";
import Modal from "~/components/Modal";
import type { Comment } from "~/types/comment";
import Loading from "~/components/Loading";
import { useAuth } from "~/context/AuthContext";
import { getApiErrorMessage, useAuth } from "~/context/AuthContext";
import { NotFound } from "~/components/NotFound";
import type { PaginatedResponse } from "~/types/PaginatedResponse";
@@ -52,6 +52,7 @@ export default function Post() {
}
const [post, setPost] = useState<Post|null>(null);
const [notFound, setNotFound] = useState(false);
const [isEditPostModalOpen, setIsEditPostModalOpen] = useState(false);
const [paginatedComments, setPaginatedComments] = useState<PaginatedResponse<Comment> | null>(null);
@@ -68,8 +69,19 @@ export default function Post() {
console.log(`Fetching post with id: ${id}`);
async function fetchData() {
const post = await API.fetchPostById(idNum);
setPost(post);
try {
const postData = await API.fetchPostById(idNum);
setPost(postData);
} catch (error: any) {
if (error.status === 404) {
setNotFound(true);
} else {
console.error("Failed to fetch post:", error);
// Optionally, you could navigate to a generic error page
// or show a toast notification.
alert(getApiErrorMessage(error));
}
}
}
fetchData();
}, [id]);
@@ -84,6 +96,12 @@ export default function Post() {
const { user } = useAuth();
if (notFound) {
return <RootLayout>
<NotFound />
</RootLayout>;
}
if (!post) {
return <RootLayout>
<Loading />
@@ -151,14 +169,18 @@ export default function Post() {
const enteredTags = tags.split(' ').filter(t => t);
if (title && description && tags) {
const res = await API.updatePost(post.id, title, description, enteredTags, user);
const data = await res.json();
if (!res.ok) {
setError(data.detail || "Failed to update post.");
return;
try {
const res = await API.updatePost(post.id, title, description, enteredTags, user);
const data = await res.json();
if (!res.ok) {
setError(getApiErrorMessage(data));
return;
}
setPost(data);
setIsEditPostModalOpen(false);
} catch (err: any) {
setError(getApiErrorMessage(err));
}
setPost(data);
setIsEditPostModalOpen(false);
}
};
@@ -203,51 +225,61 @@ export default function Post() {
*/
const onPostDelete = async () => {
if (!user) return;
const res = await API.deletePost(post.id, user)
if (!res.ok) {
alert("Failed to delete post.");
return;
try {
const res = await API.deletePost(post.id, user)
if (!res.ok) {
const err = await res.json();
alert(`Failed to delete post: ${getApiErrorMessage(err)}`);
return;
}
await navigate('/')
} catch (err: any) {
alert(`Failed to delete post: ${getApiErrorMessage(err)}`);
}
await navigate('/')
};
const onAddComment = async (postId: number, commentText: string) => {
if (!user) return;
const res = await API.postComment(postId, commentText, user);
if (!res.ok) {
alert("Failed to add comment");
return;
}
const newComment: Comment = await res.json();
try {
const res = await API.postComment(postId, commentText, user);
if (!res.ok) {
const err = await res.json();
alert(`Failed to add comment: ${getApiErrorMessage(err)}`);
return;
}
const newComment: Comment = await res.json();
// Refetch comments if this is the first one.
if (!paginatedComments || paginatedComments.totalCount === 0) {
const comments = await API.fetchCommentsByPostId(idNum, 1);
setPaginatedComments(comments);
setCurrentCommentPage(1);
return;
}
// Refetch comments if this is the first one.
if (!paginatedComments || paginatedComments.totalCount === 0) {
const comments = await API.fetchCommentsByPostId(idNum, 1);
setPaginatedComments(comments);
setCurrentCommentPage(1);
return;
}
const totalPages = Math.ceil((paginatedComments.totalCount + 1) / paginatedComments.pageSize);
if (currentCommentPage !== totalPages) {
setCurrentCommentPage(totalPages);
} else {
setPaginatedComments(prev => {
if (!prev) return null;
// If current page is not full, just add the comment
if (prev.items.length < prev.pageSize) {
return {
...prev,
items: [...prev.items, newComment],
totalCount: prev.totalCount + 1
};
} else {
// This case is unlikely if totalPages is calculated correctly, but as a fallback, we refetch.
setCurrentCommentPage(totalPages);
return prev;
}
});
const totalPages = Math.ceil((paginatedComments.totalCount + 1) / paginatedComments.pageSize);
if (currentCommentPage !== totalPages) {
setCurrentCommentPage(totalPages);
} else {
setPaginatedComments(prev => {
if (!prev) return null;
// If current page is not full, just add the comment
if (prev.items.length < prev.pageSize) {
return {
...prev,
items: [...prev.items, newComment],
totalCount: prev.totalCount + 1
};
} else {
// This case is unlikely if totalPages is calculated correctly, but as a fallback, we refetch.
setCurrentCommentPage(totalPages);
return prev;
}
});
}
} catch (err: any) {
alert(`Failed to add comment: ${getApiErrorMessage(err)}`);
}
};
@@ -258,23 +290,28 @@ export default function Post() {
const onDeleteComment = async (postId: number, commentId: number) => {
if (!user || !paginatedComments) return;
const res = await API.deleteComment(postId, commentId, user);
if (!res.ok) {
alert("Failed to delete comment.");
return;
}
setPaginatedComments(prev => {
if (!prev) return null;
const newItems = prev.items.filter(c => c.id !== commentId);
const newTotalCount = prev.totalCount - 1;
if (newItems.length === 0 && currentCommentPage > 1) {
setCurrentCommentPage(currentCommentPage - 1);
return { ...prev, items: newItems, totalCount: newTotalCount };
try {
const res = await API.deleteComment(postId, commentId, user);
if (!res.ok) {
const err = await res.json();
alert(`Failed to delete comment: ${getApiErrorMessage(err)}`);
return;
}
return { ...prev, items: newItems, totalCount: newTotalCount };
});
setPaginatedComments(prev => {
if (!prev) return null;
const newItems = prev.items.filter(c => c.id !== commentId);
const newTotalCount = prev.totalCount - 1;
if (newItems.length === 0 && currentCommentPage > 1) {
setCurrentCommentPage(currentCommentPage - 1);
return { ...prev, items: newItems, totalCount: newTotalCount };
}
return { ...prev, items: newItems, totalCount: newTotalCount };
});
} catch (err: any) {
alert(`Failed to delete comment: ${getApiErrorMessage(err)}`);
}
};
@@ -311,13 +348,17 @@ export default function Post() {
const handleSubmit: FormEventHandler = async (e) => {
e.preventDefault();
if (newText && commentToEdit && newText !== commentToEdit.text && user) {
// TODO
const res = await API.updateComment(post.id, commentToEdit.id, newText, user);
if (!res.ok) {
alert("Failed to update comment.");
return;
try {
const res = await API.updateComment(post.id, commentToEdit.id, newText, user);
if (!res.ok) {
const err = await res.json();
alert(`Failed to update comment: ${getApiErrorMessage(err)}`);
return;
}
setPaginatedComments(prev => prev ? { ...prev, items: prev.items.map(c => c.id === commentToEdit.id ? {...c, text: newText} : c) } : null);
} catch (err: any) {
alert(`Failed to update comment: ${getApiErrorMessage(err)}`);
}
setPaginatedComments(prev => prev ? { ...prev, items: prev.items.map(c => c.id === commentToEdit.id ? {...c, text: newText} : c) } : null);
}
setIsEditCommentModalOpen(false)
};