This commit is contained in:
payacom
2026-07-10 18:17:11 +03:30
parent ce1becc392
commit 2863cdf490
13 changed files with 121 additions and 78 deletions

View File

@@ -16,11 +16,9 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
const description =
post?.caption?.slice(0, 160) ||
"مشاهده پست در مادستاگرام — پلتفرم مدلینگ و تبلیغات";
const imagePath = post?.files?.[0]?.path?.replace(
"/root/modstagram-back/storage",
""
);
const ogImage = imagePath ? `${IMAGE_BASE_URL}${imagePath}` : undefined;
const ogImage = post?.files?.[0]?.path
? buildStorageUrl(post.files[0].path)
: undefined;
return {
title,

View File

@@ -253,34 +253,52 @@ function TicketChat({ params }: ITicketChatProps) {
}
}
const formData = new FormData();
formData.append("content", textContent);
formData.append("receiverId", targetReceiverId);
formData.append("senderId", currentUserId);
if (replySource?._id && !replySource._id.startsWith("temp")) {
formData.append("replyToId", replySource._id);
}
if (fileForUpload) {
formData.append("file", fileForUpload);
if (fileType) formData.append("fileType", fileType);
}
if (selfDestructSeconds) {
formData.append("selfDestructSeconds", String(selfDestructSeconds));
}
if (applyViewOnce) {
formData.append("viewOnce", "true");
}
const isFileUpload = fileForUpload && fileType !== "location";
try {
const endpoint =
fileToUpload && fileType !== "location" ? "/chat/file" : "/chat";
let response: ChatMessage | { data: ChatMessage };
const response = await request<ChatMessage | { data: ChatMessage }>(
"post",
endpoint,
formData,
{ headers: { "Content-Type": "multipart/form-data" } }
);
if (isFileUpload) {
const formData = new FormData();
formData.append("content", textContent);
formData.append("receiverId", targetReceiverId);
formData.append("senderId", currentUserId);
if (replySource?._id && !replySource._id.startsWith("temp")) {
formData.append("replyToId", replySource._id);
}
formData.append("file", fileForUpload);
if (fileType) formData.append("fileType", fileType);
if (selfDestructSeconds) {
formData.append("selfDestructSeconds", String(selfDestructSeconds));
}
if (applyViewOnce) {
formData.append("viewOnce", "true");
}
response = await request<ChatMessage | { data: ChatMessage }>(
"post",
"/chat/file",
formData
);
} else {
const payload: Record<string, unknown> = {
content: textContent,
receiverId: targetReceiverId,
senderId: currentUserId,
};
if (replySource?._id && !replySource._id.startsWith("temp")) {
payload.replyToId = replySource._id;
}
if (selfDestructSeconds) {
payload.selfDestructSeconds = selfDestructSeconds;
}
response = await request<ChatMessage | { data: ChatMessage }>(
"post",
"/chat",
payload
);
}
const serverMsg = normalizeMessage(response as ChatMessage);
const sentMsg: ChatMessage = {
@@ -289,10 +307,10 @@ function TicketChat({ params }: ITicketChatProps) {
replyTo: serverMsg.replyTo ?? replyPayload,
};
if (currentUserId && chatPartnerId) {
if (currentUserId && targetReceiverId) {
linkIds(tempId, sentMsg._id);
queryClient.setQueryData(
chatThreadQueryKey(currentUserId, chatPartnerId),
chatThreadQueryKey(currentUserId, targetReceiverId),
(oldData) => appendMessageToThreadCache(oldData, sentMsg)
);
}

View File

@@ -1,12 +1,15 @@
"use client";
import { useEffect } from "react";
import { ICON_VERSION } from "@/components/main/BaseUrl";
export default function RegisterSW() {
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/service-worker.js").then(() => {
console.log("Service Worker Registered!");
});
navigator.serviceWorker
.register(`/service-worker.js?v=${ICON_VERSION}`)
.then(() => {
console.log("Service Worker Registered!");
});
}
}, []);

View File

@@ -2,6 +2,7 @@
import { cn } from "@/lib/utils";
import BoldIcon from "@/components/ui/BoldIcon";
import { staticIconUrl } from "@/components/main/BaseUrl";
export type ChatBoldIconName =
| "timer"
@@ -20,9 +21,9 @@ export type ChatBoldIconName =
/** Chat-specific SVG assets (custom uploads) */
const CHAT_ICON_SRC: Partial<Record<ChatBoldIconName, string>> = {
timer: "/images/icons/chat/timer.svg",
search: "/images/icons/chat/search-normal.svg",
back: "/images/icons/chat/back.svg",
timer: staticIconUrl("/images/icons/chat/timer.svg"),
search: staticIconUrl("/images/icons/chat/search-normal.svg"),
back: staticIconUrl("/images/icons/chat/back.svg"),
};
const ICON_NAME: Record<ChatBoldIconName, string> = {

View File

@@ -1,7 +1,7 @@
"use client";
import React, { useEffect, useState, useRef, useMemo, useCallback } from "react";
import { SOCKET_URL } from "../main/BaseUrl";
import { SOCKET_URL, getApiBaseUrl } from "../main/BaseUrl";
import { apiClient } from "@/hooks/useAxios";
import { chatThreadQueryKey } from "@/lib/chat/queryKeys";
import {
@@ -97,7 +97,9 @@ const ChatMessageList = ({
);
const senderId = normalizeThreadId(userDetail?._id ?? getStoredUserId());
const receiverId = normalizeThreadId(chatPartnerId);
const receiverId = normalizeThreadId(
userTwoDetail?._id ?? chatPartnerId
);
const threadReady = Boolean(senderId && receiverId);
const queryKey = chatThreadQueryKey(senderId, receiverId);
@@ -119,7 +121,8 @@ const ChatMessageList = ({
try {
const response = await apiClient.get(
`/chat?senderId=${sid}&receiverId=${rid}&page=${pageParam}&limit=50`
`/chat?senderId=${sid}&receiverId=${rid}&page=${pageParam}&limit=50`,
{ baseURL: getApiBaseUrl() }
);
const msgs = (response.data.messages || []) as ChatMessage[];
return {

View File

@@ -38,6 +38,18 @@ export const BASE_URL = getApiBaseUrl();
/** @deprecated از getStorageBaseUrl() استفاده کنید */
export const IMAGE_BASE_URL = getStorageBaseUrl();
/** نسخه آیکون‌ها — با هر تغییر آیکون این مقدار را افزایش دهید */
export const ICON_VERSION =
process.env.NEXT_PUBLIC_ICON_VERSION ?? "2";
/** URL آیکون استاتیک با cache-busting */
export function staticIconUrl(path: string): string {
if (!path) return "";
const normalized = path.startsWith("/") ? path : `/${path}`;
const separator = normalized.includes("?") ? "&" : "?";
return `${normalized}${separator}v=${ICON_VERSION}`;
}
/** ساخت URL کامل فایل storage — مسیرهای DB را یکسان می‌کند */
export function buildStorageUrl(path: string | null | undefined): string {
if (!path) return "";

View File

@@ -88,10 +88,7 @@ function MainModelCard({
const mediaFiles =
files?.map((file) => ({
...file,
src: `${IMAGE_BASE_URL}${file.path.replace(
"/root/modstagram-back/storage",
""
)}`,
src: buildStorageUrl(file.path),
})) || [];
// تابع برای ایجاد افکت ripple و تغییر وضعیت صدا

View File

@@ -71,10 +71,7 @@ function MainModelCard({ postData }: { postData: Post }) {
const mediaFiles =
files?.map((file) => ({
...file,
src: `${IMAGE_BASE_URL}${file.path.replace(
"/root/modstagram-back/storage",
""
)}`,
src: buildStorageUrl(file.path),
})) || [];
const sliderSettings = {

View File

@@ -92,7 +92,7 @@ function ModelContentPosts({ filters, token, _id }: InfinitePostsProps) {
const mediaFiles =
post.files?.map((file) => ({
...file,
src: `${IMAGE_BASE_URL}${file.path.replace("/root/modstagram-back/storage", "")}`,
src: buildStorageUrl(file.path),
})) || [];
const firstMedia = mediaFiles[0]?.src || "/images/placeholder.png";

View File

@@ -64,10 +64,7 @@ export default function ReelsPostCard({
const mediaFiles =
files?.map((file) => ({
...file,
src: `${IMAGE_BASE_URL}${file.path.replace(
"/root/modstagram-back/storage",
""
)}`,
src: buildStorageUrl(file.path),
})) || [];
const handleVideoClick = (e: React.MouseEvent<HTMLVideoElement>) => {

View File

@@ -1,6 +1,7 @@
"use client";
import { cn } from "@/lib/utils";
import { staticIconUrl } from "@/components/main/BaseUrl";
const BOLD_BASE = "/images/icons/vuesax/bold";
@@ -17,7 +18,7 @@ interface BoldIconProps {
export function boldIconUrl(name: string): string {
const file = name.endsWith(".svg") ? name : `${name}.svg`;
return `${BOLD_BASE}/${file}`;
return staticIconUrl(`${BOLD_BASE}/${file}`);
}
export default function BoldIcon({