Files
nahodka-front/src/hooks/useFeedActions.jsx
2026-08-18 01:06:50 +03:00

110 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/hooks/useFeedActions.js
import { useState, useCallback, useRef } from "react";
import { useDeletePost } from "./useDeletePost";
import { useFavorites } from "./useFavorites";
import { usePostStatus } from "./usePostStatus";
/**
* Объединяет действия над объявлениями (лайк, удаление, отметка "найдено")
* и общий паттерн "выполнить действие -> перезагрузить ленту и мои объявления".
*
* Авторизация (Authorization header + авто-обновление токена) обрабатывается
* внутри apiFetch — сам token сюда передавать не нужно, только isAuthenticated
* как ранняя проверка перед запросом.
*
* Каждое действие (deletePost/toggleFavorite/markAsFound) возвращает
* { success, error } напрямую из вызова — это исключает гонку между
* обновлением error state дочернего хука и его чтением здесь же в том же
* тике (React batching иначе мог бы подставить устаревшее значение).
*
* @param {Function} refresh — перезагрузить текущий список (лента/город)
* @param {Function} loadMyPosts — перезагрузить "мои объявления"
* @param {Function} onAuthRequired — вызывается когда нужна авторизация
* @param {boolean} isAuthenticated
*/
export function useFeedActions({ refresh, loadMyPosts, onAuthRequired, isAuthenticated, t, setLiked, setLikeCountAdj }) {
const { deletePost } = useDeletePost();
const { toggleFavorite } = useFavorites();
const { markAsFound } = usePostStatus();
const [error, setError] = useState("");
const likedRef = useRef(new Set());
const syncLikedRef = useCallback((prev) => {
likedRef.current = prev instanceof Set ? prev : new Set(prev);
return prev;
}, []);
const afterMutation = useCallback(() => {
refresh();
loadMyPosts();
}, [refresh, loadMyPosts]);
const toggleLike = useCallback(async (id) => {
if (!isAuthenticated) {
onAuthRequired();
return;
}
if (setLiked) {
const wasCurrentlyLiked = likedRef.current.has(id);
const delta = wasCurrentlyLiked ? -1 : 1;
setLiked(prev => {
const next = new Set(prev);
if (next.has(id)) next.delete(id); else next.add(id);
likedRef.current = next;
return next;
});
if (setLikeCountAdj) {
setLikeCountAdj(prev => ({ ...prev, [id]: (prev[id] || 0) + delta }));
}
const result = await toggleFavorite(id);
if (!result.success) {
setLiked(prev => {
const next = new Set(prev);
if (next.has(id)) next.delete(id); else next.add(id);
likedRef.current = next;
return next;
});
if (setLikeCountAdj) {
setLikeCountAdj(prev => ({ ...prev, [id]: (prev[id] || 0) - delta }));
}
setError(result.error);
}
}
}, [isAuthenticated, onAuthRequired, toggleFavorite, setLiked, setLikeCountAdj]);
const handleDelete = useCallback(async (postId) => {
if (!window.confirm(t('feed.confirmDelete'))) return;
const result = await deletePost(postId);
if (result.success) {
afterMutation();
} else {
setError(result.error);
}
}, [t, deletePost, afterMutation]);
const handleMarkFound = useCallback(async (postId) => {
if (!window.confirm(t('feed.confirmMarkFound'))) return;
const result = await markAsFound(postId);
if (result.success) {
afterMutation();
} else {
setError(result.error);
}
}, [t, markAsFound, afterMutation]);
const clearError = useCallback(() => setError(""), []);
return {
toggleLike,
handleDelete,
handleMarkFound,
handleNewAd: afterMutation,
handleEditSave: afterMutation,
syncLiked: syncLikedRef,
error,
clearError,
};
}