фикс <был недавно>
This commit is contained in:
@@ -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)}`
|
||||||
|
|||||||
Reference in New Issue
Block a user