фикс <был недавно>

This commit is contained in:
max
2026-04-17 22:24:09 +03:00
parent c8b0384392
commit bccbf12a11
2 changed files with 33 additions and 6 deletions
@@ -13,7 +13,7 @@ import Avatar from '../../../../core/presentation/components/ui/Avatar';
import type { Chat } from '../../../../core/domain/types'; import type { Chat } from '../../../../core/domain/types';
import { UserApi } from '../../../users/infrastructure/userApi'; import { UserApi } from '../../../users/infrastructure/userApi';
const userStatusCache = new Map<string, { emoji?: string | null; text?: string | null }>(); const userStatusCache = new Map<string, { emoji?: string | null; text?: string | null; isInvisible?: boolean }>();
interface ChatListItemProps { interface ChatListItemProps {
chat: Chat; chat: Chat;
@@ -27,7 +27,7 @@ function ChatListItem({ chat, isActive }: ChatListItemProps) {
const [ctxMenu, setCtxMenu] = useState<{ x: number; y: number } | null>(null); const [ctxMenu, setCtxMenu] = useState<{ x: number; y: number } | null>(null);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [otherUserStatus, setOtherUserStatus] = useState<{ emoji?: string | null; text?: string | null } | null>(null); const [otherUserStatus, setOtherUserStatus] = useState<{ emoji?: string | null; text?: string | null; isInvisible?: boolean } | null>(null);
const ctxRef = useRef<HTMLDivElement>(null); const ctxRef = useRef<HTMLDivElement>(null);
const myMember = chat.members.find((m) => m.user.id === user?.id); const myMember = chat.members.find((m) => m.user.id === user?.id);
@@ -49,7 +49,7 @@ function ChatListItem({ chat, isActive }: ChatListItemProps) {
? otherMember?.user.avatarUrl || otherMember?.user.avatar || null ? otherMember?.user.avatarUrl || otherMember?.user.avatar || null
: chat.avatarUrl || chat.avatar || null; : chat.avatarUrl || chat.avatar || null;
const isOnline = chat.type === 'personal' && !user?.isInvisible && !!otherMember?.user.isOnline; const isOnline = chat.type === 'personal' && !user?.isInvisible && !otherUserStatus?.isInvisible && !!otherMember?.user.isOnline;
// Check if someone is typing in this chat // Check if someone is typing in this chat
const typingInChat = typingUsers.filter((t) => t.chatId === chat.id && t.userId !== user?.id); const typingInChat = typingUsers.filter((t) => t.chatId === chat.id && t.userId !== user?.id);
@@ -108,7 +108,7 @@ function ChatListItem({ chat, isActive }: ChatListItemProps) {
UserApi.getUser(otherId) UserApi.getUser(otherId)
.then((profile) => { .then((profile) => {
if (cancelled) return; if (cancelled) return;
const status = { emoji: profile.statusEmoji, text: profile.statusText }; const status = { emoji: profile.statusEmoji, text: profile.statusText, isInvisible: !!profile.isInvisible };
userStatusCache.set(otherId, status); userStatusCache.set(otherId, status);
setOtherUserStatus(status); setOtherUserStatus(status);
}) })
@@ -24,6 +24,7 @@ import { useChatStore } from '../../application/chatStore';
import { httpClient } from '../../../../core/infrastructure/httpClient'; import { httpClient } from '../../../../core/infrastructure/httpClient';
import { useAuthStore } from '../../../auth/application/authStore'; import { useAuthStore } from '../../../auth/application/authStore';
import { ChatApi } from '../../infrastructure/chatApi'; import { ChatApi } from '../../infrastructure/chatApi';
import { UserApi } from '../../../users/infrastructure/userApi';
import { getSocket } from '../../../../core/infrastructure/socket'; import { getSocket } from '../../../../core/infrastructure/socket';
import { isChatMuted, toggleMuteChat } from '../../../../core/utils/sounds'; import { isChatMuted, toggleMuteChat } from '../../../../core/utils/sounds';
import { useLang } from '../../../../core/infrastructure/i18n'; import { useLang } from '../../../../core/infrastructure/i18n';
@@ -75,6 +76,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
const [confirmAction, setConfirmAction] = useState<{ message: string; action: () => void } | null>(null); const [confirmAction, setConfirmAction] = useState<{ message: string; action: () => void } | null>(null);
const [scrollReady, setScrollReady] = useState(false); const [scrollReady, setScrollReady] = useState(false);
const [activeGroupCallParticipants, setActiveGroupCallParticipants] = useState<string[]>([]); const [activeGroupCallParticipants, setActiveGroupCallParticipants] = useState<string[]>([]);
const [otherUserInvisible, setOtherUserInvisible] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null); const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollContainerRef = useRef<HTMLDivElement>(null); const scrollContainerRef = useRef<HTMLDivElement>(null);
@@ -182,6 +184,31 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
: chat?.avatarUrl || chat?.avatar || null; : chat?.avatarUrl || chat?.avatar || null;
const isOnline = chat?.type === 'personal' && otherMember?.user.isOnline; const isOnline = chat?.type === 'personal' && otherMember?.user.isOnline;
const privacyExchangeMode = !!user?.isInvisible; const privacyExchangeMode = !!user?.isInvisible;
const shouldMaskOtherUserPresence = privacyExchangeMode || otherUserInvisible;
useEffect(() => {
if (chat?.type !== 'personal' || !otherMember?.user.id) {
setOtherUserInvisible(false);
return;
}
let cancelled = false;
UserApi.getUser(otherMember.user.id)
.then((profile) => {
if (!cancelled) {
setOtherUserInvisible(!!profile.isInvisible);
}
})
.catch(() => {
if (!cancelled) {
setOtherUserInvisible(false);
}
});
return () => {
cancelled = true;
};
}, [chat?.type, otherMember?.user.id]);
const typingInChat = typingUsers.filter((t) => t.chatId === activeChat && t.userId !== user?.id); const typingInChat = typingUsers.filter((t) => t.chatId === activeChat && t.userId !== user?.id);
@@ -846,7 +873,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
src={chatAvatar} src={chatAvatar}
name={chatName} name={chatName}
size="md" size="md"
online={isOnline ? true : undefined} online={!shouldMaskOtherUserPresence && isOnline ? true : undefined}
className="avatar-knot-container group-hover:border-primary/30" className="avatar-knot-container group-hover:border-primary/30"
/> />
)} )}
@@ -860,7 +887,7 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
? <span className="text-primary font-black animate-pulse">{t('typing')}</span> ? <span className="text-primary font-black animate-pulse">{t('typing')}</span>
: isOnline : isOnline
? <span className="text-success">{t('online')}</span> ? <span className="text-success">{t('online')}</span>
: chat.type === 'personal' && privacyExchangeMode : chat.type === 'personal' && shouldMaskOtherUserPresence
? t('wasRecently') ? t('wasRecently')
: chat.type === 'personal' && otherMember?.user.lastSeen : chat.type === 'personal' && otherMember?.user.lastSeen
? `${formatLastSeen(otherMember.user.lastSeen, lang)}` ? `${formatLastSeen(otherMember.user.lastSeen, lang)}`