This commit is contained in:
2025-10-19 20:31:29 +03:00
Unverified
parent 66f0b756a0
commit f5527ca14e
67 changed files with 891 additions and 902 deletions
@@ -33,7 +33,7 @@ function ChatHeaderText({ panel }: { panel: MessagePanel | null }) {
if (panel instanceof DMPanel) {
const recipientId = panel.getRecipientId()!;
const isTyping = chat.dmTypingUsers.get(recipientId);
content = isTyping ? <TypingIndicator typingUsers={[]} /> : <OnlineStatus userId={recipientId} />;
} else if (panel instanceof PublicChatPanel && otherTypingUsers.length > 0) {
content = <TypingIndicator typingUsers={otherTypingUsers} />;
@@ -90,12 +90,12 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
useEffect(() => {
if (panel) {
setPanelState(panel.getState());
// Store the handler for cleanup
panel.onStateChange = (newState: MessagePanelState) => {
setPanelState(newState);
};
// Set up WebSocket message handler for this panel
if (panel.handleWebSocketMessage) {
setGlobalMessageHandler((message: WebSocketMessage<any>) => panel.handleWebSocketMessage(message));
@@ -104,7 +104,7 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
setPanelState(null);
setGlobalMessageHandler(null);
}
return () => {
if (panel) {
if (panel.onStateChange) {
@@ -122,11 +122,11 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
useEffect(() => {
if (chat.isSwitching) {
setSwitchOut(true);
// Use animation event listeners instead of hardcoded delays
function handleAnimationEnd(event: Event) {
const animationEvent = event as AnimationEvent;
if (animationEvent.animationName === 'fadeOutUp') {
// Apply pending panel exactly at the boundary between animations
applyPendingPanel();
@@ -138,10 +138,10 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
chat.setIsSwitching(false);
}
};
// Add event listener to document to catch all animation events
document.addEventListener('animationend', handleAnimationEnd);
// Cleanup function
return () => {
document.removeEventListener('animationend', handleAnimationEnd);
@@ -152,9 +152,9 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
// Load messages when panel changes and animation is not running
useEffect(() => {
if (!chat.activePanel || chat.isSwitching || switchOut || switchIn) return;
const panelState = chat.activePanel.getState();
if (panelState.messages.length === 0 && !panelState.isLoading) {
chat.activePanel.loadMessages();
}
@@ -180,7 +180,7 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
const id = requestAnimationFrame(() => {
el.scrollIntoView({ behavior: "smooth", block: "end" });
});
return () => cancelAnimationFrame(id);
}
@@ -193,7 +193,7 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
const dmPanel = panel as DMPanel;
const userId = dmPanel.getDMUserId();
const username = dmPanel.getDMUsername();
if (userId && username) {
initiateCall(userId, username);
}
@@ -202,7 +202,7 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
async function handleProfileClick() {
if (!panel) return;
try {
const profileData = await panel.getProfile();
if (profileData) {
@@ -215,9 +215,9 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
return (
<div className={`chat-container ${switchIn ? "chat-switch-in" : ""} ${switchOut ? "chat-switch-out" : ""}`}>
<div
<div
ref={messagePanelRef}
className="chat-main"
className="chat-main"
id="chat-inner"
onDragEnter={panel ? (e) => {
if (!e.dataTransfer) return;
@@ -252,9 +252,9 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
dragCounterRef.current = 0;
} : undefined}>
<div className="chat-header">
<img
src={panelState?.profilePicture || defaultAvatar}
alt="Avatar"
<img
src={panelState?.profilePicture || defaultAvatar}
alt="Avatar"
className="chat-header-avatar"
onClick={handleProfileClick}
style={{ cursor: panel ? "pointer" : "default" }}
@@ -272,10 +272,10 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
{panelState?.isLoading ? (
<div className="chat-messages" id="chat-messages">
<div style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
<div style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
color: "var(--mdui-color-on-surface-variant)"
}}>
@@ -283,9 +283,9 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
</div>
</div>
) : panelState && panel ? (
<ChatMessages
messages={panelState.messages}
isDm={panel.isDm()}
<ChatMessages
messages={panelState.messages}
isDm={panel.isDm()}
dmRecipientPublicKey={(panel as DMPanel).dmData?.publicKey}
onReplySelect={(message) => {
if (editMessage || editVisible) {
@@ -310,10 +310,10 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
</ChatMessages>
) : (
<div className="chat-messages" id="chat-messages">
<div style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
<div style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
color: "var(--mdui-color-on-surface-variant)"
}}>
@@ -324,10 +324,10 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
{panel && (
<>
<AnimatedOpacity
visible={isDragging}
className="file-overlay"
onDragOver={(e) => e.preventDefault()}
<AnimatedOpacity
visible={isDragging}
className="file-overlay"
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => e.preventDefault()}>
<div className="file-overlay-wrapper">
<div className="file-overlay-inner">
@@ -336,13 +336,13 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
</div>
</div>
</AnimatedOpacity>
<ChatInputWrapper
<ChatInputWrapper
onSendMessage={(text, files) => {
panel.handleSendMessage(text, replyTo?.id, files);
setReplyTo(null);
}}
}}
onSaveEdit={(content) => {
if (editMessage) {
panel.handleEditMessage(editMessage.id, content);
@@ -397,7 +397,7 @@ export function MessagePanelRenderer({ panel }: MessagePanelRendererProps) {
</>
)}
</div>
{/* Profile Dialog */}
<ProfileDialog />
</div>