fix: relocate typing indicator into the input bubble
Move TypingIndicator from document flow (between MessageList and MessageInput) into MessageInput's outer wrapper using absolute bottom-full positioning. Fixes indicator being hidden behind the floating glass input bubble on desktop.
This commit is contained in:
@@ -3,6 +3,7 @@ import { useChatStore } from '../../stores/chatStore';
|
|||||||
import { isDmChannel, getChannelOrigin, getApiForOrigin, useServerStore } from '../../stores/serverStore';
|
import { isDmChannel, getChannelOrigin, getApiForOrigin, useServerStore } from '../../stores/serverStore';
|
||||||
import { wsSend } from '../../hooks/useWebSocket';
|
import { wsSend } from '../../hooks/useWebSocket';
|
||||||
import { MentionPopover } from './MentionPopover';
|
import { MentionPopover } from './MentionPopover';
|
||||||
|
import { TypingIndicator } from './TypingIndicator';
|
||||||
import type { MemberWithUser } from '@backspace/shared';
|
import type { MemberWithUser } from '@backspace/shared';
|
||||||
|
|
||||||
interface MessageInputProps {
|
interface MessageInputProps {
|
||||||
@@ -228,7 +229,8 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-pip-obstacle="bottom" className="px-3 pb-3 flex-shrink-0 md:absolute md:bottom-3 md:left-3 md:right-3 md:z-[110] md:px-0 md:pb-0 md:glass-bubble md:rounded-[14px]">
|
<div data-pip-obstacle="bottom" className="relative px-3 pb-3 flex-shrink-0 md:absolute md:bottom-3 md:left-3 md:right-3 md:z-[110] md:px-0 md:pb-0 md:glass-bubble md:rounded-[14px]">
|
||||||
|
<TypingIndicator channelId={channelId} />
|
||||||
{replyTo && (
|
{replyTo && (
|
||||||
<div className="bg-interactive-hover rounded-t-lg px-4 py-2 flex items-center justify-between border-b border-white/[0.06]">
|
<div className="bg-interactive-hover rounded-t-lg px-4 py-2 flex items-center justify-between border-b border-white/[0.06]">
|
||||||
<div className="flex items-center gap-1 text-[14px] text-txt-message truncate">
|
<div className="flex items-center gap-1 text-[14px] text-txt-message truncate">
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function TypingIndicator({ channelId }: TypingIndicatorProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-[24px] px-4 flex items-center text-[12px] text-txt-primary font-medium select-none pointer-events-none">
|
<div className="absolute bottom-full left-1 md:left-4 mb-1 px-3 flex items-center text-[12px] text-txt-secondary font-medium select-none pointer-events-none animate-typing-in motion-reduce:animate-none">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex gap-[2px] bg-surface-elevated/20 rounded-full px-2 py-1">
|
<div className="flex gap-[2px] bg-surface-elevated/20 rounded-full px-2 py-1">
|
||||||
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '0ms', animationDuration: '0.8s' }} />
|
<div className="w-[5px] h-[5px] bg-txt-message rounded-full animate-bounce" style={{ animationDelay: '0ms', animationDuration: '0.8s' }} />
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useUIStore } from '../../stores/uiStore';
|
|||||||
import { useAuthStore } from '../../stores/authStore';
|
import { useAuthStore } from '../../stores/authStore';
|
||||||
import { MessageList } from '../chat/MessageList';
|
import { MessageList } from '../chat/MessageList';
|
||||||
import { MessageInput } from '../chat/MessageInput';
|
import { MessageInput } from '../chat/MessageInput';
|
||||||
import { TypingIndicator } from '../chat/TypingIndicator';
|
|
||||||
import { VoiceGrid } from '../voice/VoiceGrid';
|
import { VoiceGrid } from '../voice/VoiceGrid';
|
||||||
import { VoiceControlBar } from '../voice/VoiceControlBar';
|
import { VoiceControlBar } from '../voice/VoiceControlBar';
|
||||||
import { VoiceChatPanel } from '../voice/VoiceChatPanel';
|
import { VoiceChatPanel } from '../voice/VoiceChatPanel';
|
||||||
@@ -210,7 +209,6 @@ export function MainContent() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MessageList channelId={currentChannelId} />
|
<MessageList channelId={currentChannelId} />
|
||||||
<TypingIndicator channelId={currentChannelId} />
|
|
||||||
<MessageInput channelId={currentChannelId} channelName={`@${dmName}`} />
|
<MessageInput channelId={currentChannelId} channelName={`@${dmName}`} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -357,7 +355,6 @@ export function MainContent() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MessageList channelId={currentChannelId} />
|
<MessageList channelId={currentChannelId} />
|
||||||
<TypingIndicator channelId={currentChannelId} />
|
|
||||||
<MessageInput channelId={currentChannelId} channelName={channel.name} />
|
<MessageInput channelId={currentChannelId} channelName={channel.name} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MessageList } from '../chat/MessageList';
|
import { MessageList } from '../chat/MessageList';
|
||||||
import { MessageInput } from '../chat/MessageInput';
|
import { MessageInput } from '../chat/MessageInput';
|
||||||
import { TypingIndicator } from '../chat/TypingIndicator';
|
|
||||||
import { useUIStore } from '../../stores/uiStore';
|
import { useUIStore } from '../../stores/uiStore';
|
||||||
|
|
||||||
interface VoiceChatPanelProps {
|
interface VoiceChatPanelProps {
|
||||||
@@ -31,9 +30,6 @@ export function VoiceChatPanel({ channelId, channelName }: VoiceChatPanelProps)
|
|||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
<MessageList channelId={channelId} />
|
<MessageList channelId={channelId} />
|
||||||
|
|
||||||
{/* Typing indicator */}
|
|
||||||
<TypingIndicator channelId={channelId} />
|
|
||||||
|
|
||||||
{/* Input */}
|
{/* Input */}
|
||||||
<MessageInput channelId={channelId} channelName={channelName} />
|
<MessageInput channelId={channelId} channelName={channelName} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -238,6 +238,14 @@
|
|||||||
animation: slideUp 0.15s ease-out;
|
animation: slideUp 0.15s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes typingFadeIn {
|
||||||
|
from { transform: translateY(4px); opacity: 0; }
|
||||||
|
to { transform: translateY(0); opacity: 1; }
|
||||||
|
}
|
||||||
|
.animate-typing-in {
|
||||||
|
animation: typingFadeIn 0.15s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes gradientPulse {
|
@keyframes gradientPulse {
|
||||||
0%, 100% { opacity: 0.7; }
|
0%, 100% { opacity: 0.7; }
|
||||||
50% { opacity: 1; }
|
50% { opacity: 1; }
|
||||||
|
|||||||
Reference in New Issue
Block a user