This commit is contained in:
mahmoodsht 2026-05-21 18:13:53 +03:30
parent 213c0a70f0
commit a535e43e81
8 changed files with 155 additions and 114 deletions

File diff suppressed because one or more lines are too long

2
dist/index.html vendored
View File

@ -16,7 +16,7 @@
background: #23183E; background: #23183E;
} }
</style> </style>
<script type="module" crossorigin src="/assets/index-B28_Ysnv.js"></script> <script type="module" crossorigin src="/assets/index-Dvo_8Nz_.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B5jzgFDg.css"> <link rel="stylesheet" crossorigin href="/assets/index-B5jzgFDg.css">
</head> </head>

View File

@ -88,7 +88,7 @@ export function AvatarSelectionModal({
// حالا فایل را به سرور آپلود می‌کنیم // حالا فایل را به سرور آپلود می‌کنیم
if (fileToUpload) { if (fileToUpload) {
console.log("Starting image upload...", fileToUpload.name); console.log("Starting image upload...", fileToUpload.name);
const filename = await uploadImage(fileToUpload); const filename = await uploadImage(fileToUpload, { requireResize: true });
console.log("Upload result:", filename); console.log("Upload result:", filename);
if (filename) { if (filename) {

View File

@ -489,7 +489,7 @@ export function CommentsModal({
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
className="fixed inset-0 z-[60] flex items-center justify-center p-4" className="fixed inset-0 z-[130] flex items-center justify-center p-4"
style={{ backgroundColor: "rgba(0, 0, 0, 0.7)" }} style={{ backgroundColor: "rgba(0, 0, 0, 0.7)" }}
onClick={() => setDeleteConfirmId(null)} onClick={() => setDeleteConfirmId(null)}
> >

View File

@ -8,7 +8,7 @@ import { useEffect, useState } from "react";
import { getUserProfile, getCachedProfile, saveUserProfile, getUserProfileData, type UserProfile, type ProfileChallenge, type ProfileCoinTransaction, type ProfilePost } from "../../services/profileService"; import { getUserProfile, getCachedProfile, saveUserProfile, getUserProfileData, type UserProfile, type ProfileChallenge, type ProfileCoinTransaction, type ProfilePost } from "../../services/profileService";
import { PostCard } from "./PostCard"; import { PostCard } from "./PostCard";
import { AvatarSelectionModal } from "./AvatarSelectionModal"; import { AvatarSelectionModal } from "./AvatarSelectionModal";
import { getProfileImageUrl, getFeedImageUrl, getVideoUrl, getAudioUrl, isImageFile, getMagicBagFileUrl, getAvatarUrl } from "../../services/feedService"; import { getProfileImageUrl, getFeedImageUrl, getVideoUrl, getAudioUrl, isImageFile, getMagicBagFileUrl, getAvatarUrl, bumpAvatarCacheBust } from "../../services/feedService";
import { ImageWithFallback } from "./figma/ImageWithFallback"; import { ImageWithFallback } from "./figma/ImageWithFallback";
import { usePageTracking } from "../../hooks/usePageTracking"; import { usePageTracking } from "../../hooks/usePageTracking";
import { getMissionTypeToTopicId } from "../../utils/topicMapper"; import { getMissionTypeToTopicId } from "../../utils/topicMapper";
@ -165,6 +165,9 @@ export function ProfilePage() {
console.log("Saving profile with data:", JSON.stringify(saveData)); console.log("Saving profile with data:", JSON.stringify(saveData));
const result = await saveUserProfile(saveData); const result = await saveUserProfile(saveData);
console.log("Save profile result:", result); console.log("Save profile result:", result);
if (result) {
bumpAvatarCacheBust();
}
// بارگذاری مجدد پروفایل از سرور // بارگذاری مجدد پروفایل از سرور
console.log("Reloading profile from server..."); console.log("Reloading profile from server...");

View File

@ -13,6 +13,15 @@ import { usePageTracking } from "../../hooks/usePageTracking";
import { toPersianDigits } from "../../utils/persianNumberUtils"; import { toPersianDigits } from "../../utils/persianNumberUtils";
import chatbotAvatarIcon from "../../assets/chatbot-bot-avatar.png"; import chatbotAvatarIcon from "../../assets/chatbot-bot-avatar.png";
const createMessageId = () => {
if (typeof globalThis !== "undefined" && globalThis.crypto?.randomUUID) {
return globalThis.crypto.randomUUID();
}
const randomPart = Math.random().toString(36).slice(2, 10);
return `msg-${Date.now()}-${randomPart}`;
};
export function PublicChatPage() { export function PublicChatPage() {
usePageTracking("چت عمومی"); usePageTracking("چت عمومی");
@ -69,7 +78,7 @@ export function PublicChatPage() {
publicMessages.forEach((msg) => { publicMessages.forEach((msg) => {
if (msg.question) { if (msg.question) {
converted.push({ converted.push({
id: crypto.randomUUID(), id: createMessageId(),
type: "user", type: "user",
content: msg.question, content: msg.question,
timestamp: toPersianDigits(msg.datetime1), timestamp: toPersianDigits(msg.datetime1),
@ -78,7 +87,7 @@ export function PublicChatPage() {
if (msg.answer) { if (msg.answer) {
converted.push({ converted.push({
id: crypto.randomUUID(), id: createMessageId(),
type: "other", type: "other",
content: msg.answer, content: msg.answer,
author: "ربات", author: "ربات",
@ -119,7 +128,7 @@ export function PublicChatPage() {
if (!trimmedText || isSending) return; if (!trimmedText || isSending) return;
const userMessage: ChatMessage = { const userMessage: ChatMessage = {
id: crypto.randomUUID(), id: createMessageId(),
type: "user", type: "user",
content: trimmedText, content: trimmedText,
timestamp: new Date().toLocaleTimeString("fa-IR", { timestamp: new Date().toLocaleTimeString("fa-IR", {
@ -128,7 +137,7 @@ export function PublicChatPage() {
}), }),
}; };
const loadingMessageId = crypto.randomUUID(); const loadingMessageId = createMessageId();
const loadingMessage: ChatMessage = { const loadingMessage: ChatMessage = {
id: loadingMessageId, id: loadingMessageId,
type: "loading", type: "loading",
@ -152,7 +161,7 @@ export function PublicChatPage() {
} }
const botMessage: ChatMessage = { const botMessage: ChatMessage = {
id: crypto.randomUUID(), id: createMessageId(),
type: "other", type: "other",
content: result.answer, content: result.answer,
author: "ربات", author: "ربات",

View File

@ -1,6 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { getFluentEmojiCDN } from "@lobehub/fluent-emoji/es/getFluentEmojiCDN";
const emojiRegex = const emojiRegex =
/(\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?)*)/gu; /(\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?)*)/gu;
@ -9,6 +8,15 @@ interface EmojiTextProps {
text: string; text: string;
} }
function getFluentEmojiCDN(emoji: string) {
const code = Array.from(emoji)
.map((char) => char.codePointAt(0)?.toString(16))
.filter(Boolean)
.join("-");
return `https://cdn.jsdelivr.net/gh/microsoft/fluentui-emoji/assets/${code}/3D/${code}.png`;
}
function FluentEmojiImage({ emoji }: { emoji: string }) { function FluentEmojiImage({ emoji }: { emoji: string }) {
const [failed, setFailed] = useState(false); const [failed, setFailed] = useState(false);
@ -22,7 +30,7 @@ function FluentEmojiImage({ emoji }: { emoji: string }) {
return ( return (
<img <img
src={getFluentEmojiCDN(emoji, { cdn: "unpkg", type: "3d" })} src={getFluentEmojiCDN(emoji)}
alt={emoji} alt={emoji}
loading="lazy" loading="lazy"
onError={() => setFailed(true)} onError={() => setFailed(true)}

View File

@ -1,5 +1,17 @@
import { API_BASE_URL } from "../config/api"; import { API_BASE_URL } from "../config/api";
const AVATAR_CACHE_BUST_KEY = "avatarCacheBust";
const getAvatarCacheBustValue = (): string => {
return localStorage.getItem(AVATAR_CACHE_BUST_KEY) || "";
};
export const bumpAvatarCacheBust = (): string => {
const value = Date.now().toString();
localStorage.setItem(AVATAR_CACHE_BUST_KEY, value);
return value;
};
// Get token from localStorage // Get token from localStorage
export const getAuthToken = (): string | null => { export const getAuthToken = (): string | null => {
const accessToken = localStorage.getItem("accessToken"); const accessToken = localStorage.getItem("accessToken");
@ -143,7 +155,9 @@ export const getAvatarUrl = (personStageId: string): string => {
// Get profile image URL from filename // Get profile image URL from filename
export const getProfileImageUrl = (filename: string, userStageId: string): string => { export const getProfileImageUrl = (filename: string, userStageId: string): string => {
const token = getAuthToken(); const token = getAuthToken();
return `${API_BASE_URL}/api/getimage?stageID=${userStageId}&nameOrID=image&token=${token}`; const version = encodeURIComponent(filename || "image");
const cacheBust = encodeURIComponent(getAvatarCacheBustValue());
return `${API_BASE_URL}/api/getimage?stageID=${userStageId}&nameOrID=image&token=${token}&v=${version}&cb=${cacheBust}`;
}; };
// Get image URL for feed content // Get image URL for feed content
@ -637,8 +651,12 @@ export const uploadFile = async (file: File): Promise<string | null> => {
}; };
// Upload Image // Upload Image
export const uploadImage = async (file: File): Promise<string | null> => { export const uploadImage = async (
file: File,
options: { requireResize?: boolean } = {}
): Promise<string | null> => {
const token = getAuthToken(); const token = getAuthToken();
const { requireResize = false } = options;
if (!token) { if (!token) {
console.error("uploadImage: No auth token found"); console.error("uploadImage: No auth token found");
@ -659,6 +677,10 @@ export const uploadImage = async (file: File): Promise<string | null> => {
console.log("uploadImage: Image resized successfully"); console.log("uploadImage: Image resized successfully");
} catch (resizeError) { } catch (resizeError) {
console.warn("uploadImage: Failed to resize image, uploading original:", resizeError); console.warn("uploadImage: Failed to resize image, uploading original:", resizeError);
if (requireResize) {
console.error("uploadImage: Resize is required for this upload. Aborting upload.");
return null;
}
// در صورت خطا در ریسایز، فایل اصلی را آپلود می‌کنیم // در صورت خطا در ریسایز، فایل اصلی را آپلود می‌کنیم
fileToUpload = file; fileToUpload = file;
} }