...
This commit is contained in:
parent
213c0a70f0
commit
a535e43e81
File diff suppressed because one or more lines are too long
2
dist/index.html
vendored
2
dist/index.html
vendored
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -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...");
|
||||||
|
|
|
||||||
|
|
@ -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: "ربات",
|
||||||
|
|
|
||||||
|
|
@ -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)}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user