diff --git a/src/components/ImageViewer.jsx b/src/components/ImageViewer.jsx index 5db113e..12cd00a 100644 --- a/src/components/ImageViewer.jsx +++ b/src/components/ImageViewer.jsx @@ -1,7 +1,7 @@ import { useState, useCallback, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import CloseIcon from "./icons/CloseIcon"; -import { API_URL } from "../config/authConstants"; +import { getImageUrl } from "../config/authConstants"; import { useChats } from "../hooks/useChats"; import { useAuthContext } from "../context/AuthContext"; @@ -13,7 +13,7 @@ export default function ImageViewer({ images, initialIndex = 0, onClose, postDat const { createOrGetChat } = useChats(); const { isAuthenticated } = useAuthContext(); - const urls = images.map(u => (u.startsWith("http") ? u : `${API_URL}${u}`)); + const urls = images.map(u => getImageUrl(u)); const next = useCallback(() => { setIndex(i => (i + 1) % urls.length); diff --git a/src/components/PostOnFeed.jsx b/src/components/PostOnFeed.jsx index fb7f1c4..de30d74 100644 --- a/src/components/PostOnFeed.jsx +++ b/src/components/PostOnFeed.jsx @@ -8,7 +8,7 @@ import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useChats } from "../hooks/useChats"; import { useAuthContext } from "../context/AuthContext"; -import { API_URL } from "../config/authConstants"; +import { getImageUrl } from "../config/authConstants"; import DateCalendarPopup from "./DateCalendarPopup"; import { useTranslation } from 'react-i18next'; @@ -36,7 +36,7 @@ export default function PostOnFeed({ ad, liked, onLike, onEdit, onDelete, onMark console.error(err); } }; - const imageUrl = ad.images?.[0] ? `${API_URL}${ad.images[0]}` : null; + const imageUrl = getImageUrl(ad.images?.[0]); // ✅ Формируем строку адреса из city + district const address = [ad.city, ad.district].filter(Boolean).join(", "); diff --git a/src/components/profile/ProfileModal.jsx b/src/components/profile/ProfileModal.jsx index bb2cff8..3101f1a 100644 --- a/src/components/profile/ProfileModal.jsx +++ b/src/components/profile/ProfileModal.jsx @@ -24,21 +24,31 @@ export default function ProfileModal({ onClose, initialData = {} }) { const existingImageUrl = initialData.avatar || null; const { - imageFile, - displayImage, + images, imgError, compressing, fileInputRef, handleFileChange, removeImage, + getExistingUrlsToKeep, + getFilesForUpload, } = useImageUpload(existingImageUrl); + const currentImage = images[0]; + const displayImage = currentImage?.preview ?? null; + const imageFile = currentImage?.file ?? null; + // ─── Field change handler ─────────────────────────────────────────────── const handleFieldChange = useCallback((key, value) => { setForm(prev => ({ ...prev, [key]: value })); setErrors(prev => ({ ...prev, [key]: "" })); }, []); + // ─── Remove image handler (wraps removeImage with the only image id) ───── + const handleRemoveImage = useCallback(() => { + if (currentImage?.id) removeImage(currentImage.id); + }, [currentImage, removeImage]); + // ─── Phone field change with formatting ────────────────────────────────── const handlePhoneChange = useCallback((value) => { const formatted = formatPhone(value); @@ -53,13 +63,15 @@ export default function ProfileModal({ onClose, initialData = {} }) { return; } + const newAvatars = getFilesForUpload(); + const keptExisting = getExistingUrlsToKeep(); const profileData = { firstName: form.firstName.trim(), lastName: form.lastName.trim(), login: form.login.trim(), phone: form.phone.trim(), email: form.email.trim(), - avatar: imageFile || existingImageUrl, + avatar: newAvatars.length > 0 ? newAvatars[0] : (keptExisting.length > 0 ? keptExisting[0] : null), }; const updatedUser = await updateProfile(user?.id, profileData); @@ -82,7 +94,7 @@ export default function ProfileModal({ onClose, initialData = {} }) { compressing={compressing} fileInputRef={fileInputRef} onFileChange={handleFileChange} - onRemove={removeImage} + onRemove={handleRemoveImage} /> ({ id: `existing-${i}`, file: null, - preview: `${API_URL}${url}`, + preview: getImageUrl(url), isExisting: true, serverUrl: url, }))); diff --git a/src/pages/ChatListPage.jsx b/src/pages/ChatListPage.jsx index 9473e49..b9bb9fc 100644 --- a/src/pages/ChatListPage.jsx +++ b/src/pages/ChatListPage.jsx @@ -4,7 +4,7 @@ import { useChats } from "../hooks/useChats"; import { useAuthContext } from "../context/AuthContext"; import { useUnread } from "../context/UnreadContext"; import { getPostForChat } from "../stores/chatPostStore"; -import { API_URL } from "../config/authConstants"; +import { getImageUrl } from "../config/authConstants"; import { useDeleteChat } from "../hooks/useDeleteChat"; import TrashIcon from "../components/icons/TrashIcon"; import { useTranslation } from 'react-i18next'; @@ -75,7 +75,7 @@ export default function ChatListPage() { {!loading && chats.map((chat) => { const post = getPostForChat(chat.id); const title = post?.title || chat.otherUser?.name || t('chat.user'); - const imgSrc = post?.images?.[0] ? `${API_URL}${post.images[0]}` : null; + const imgSrc = getImageUrl(post?.images?.[0]); const emoji = post?.emoji; const unreadCount = unreadMap[chat.id] ?? chat.unreadCount ?? chat.unreadMessages ?? 0; return ( diff --git a/src/pages/ChatPage.jsx b/src/pages/ChatPage.jsx index f59edf7..f17d7fb 100644 --- a/src/pages/ChatPage.jsx +++ b/src/pages/ChatPage.jsx @@ -5,7 +5,7 @@ import { useChatSocket } from "../hooks/useChatSocket"; import { getPostForChat } from "../stores/chatPostStore"; import { setActiveChatId } from "../stores/activeChatStore"; import { useUnread } from "../context/UnreadContext"; -import { API_URL } from "../config/authConstants"; +import { getImageUrl } from "../config/authConstants"; import Picker from "@emoji-mart/react"; import emojiData from "@emoji-mart/data"; import { useTranslation } from 'react-i18next'; @@ -69,7 +69,7 @@ export default function ChatPage() { }; const post = getPostForChat(chatId); - const postImg = post?.images?.[0] ? `${API_URL}${post.images[0]}` : null; + const postImg = getImageUrl(post?.images?.[0]); return (
diff --git a/src/pages/MainFeed.jsx b/src/pages/MainFeed.jsx index a4fd958..f47bb98 100644 --- a/src/pages/MainFeed.jsx +++ b/src/pages/MainFeed.jsx @@ -1,7 +1,7 @@ import { useState, useEffect, useCallback, useMemo, useRef } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import "../styles/index.css"; -import { API_URL } from "../config/authConstants"; +import { API_URL, getImageUrl } from "../config/authConstants"; import Sidebar from "../components/Sidebar"; import RightSidebar from "../components/RightSidebar"; import PostOnFeed from "../components/PostOnFeed"; @@ -78,11 +78,7 @@ export default function MainFeed() { const firstName = user.firstName || nameParts[0] || ""; const lastName = user.lastName || nameParts.slice(1).join(" ") || ""; - // Аватар может быть относительным путём (/uploads/avatars/...) — добавляем API_URL - const rawAvatar = user.avatar || null; - const avatar = rawAvatar && rawAvatar.startsWith("/") - ? `${API_URL}${rawAvatar}` - : rawAvatar; + const avatar = getImageUrl(user.avatar); return { login: user.login || "", diff --git a/src/pages/ReviewsPage.jsx b/src/pages/ReviewsPage.jsx index a3c42ab..aed1e22 100644 --- a/src/pages/ReviewsPage.jsx +++ b/src/pages/ReviewsPage.jsx @@ -1,6 +1,6 @@ import { useState, useCallback, useEffect } from "react"; import { useNavigate } from "react-router-dom"; -import { API_URL } from "../config/authConstants"; +import { API_URL, getImageUrl } from "../config/authConstants"; import { useAuthContext } from "../context/AuthContext"; import MenuIcon from "../components/icons/MenuIcon"; import Sidebar from "../components/Sidebar"; @@ -140,7 +140,7 @@ export default function ReviewsPage() { lastName: user.lastName || (user.name || "").trim().split(/\s+/).slice(1).join(" ") || "", email: user.email || "", phone: user.phone || "", - avatar: user.avatar && user.avatar.startsWith("/") ? `${API_URL}${user.avatar}` : user.avatar || null, + avatar: getImageUrl(user.avatar), id: user.id, } : {};