feat(mobile): add MobileChatScreen with back nav and member list access
This commit is contained in:
@@ -0,0 +1,86 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useUIStore } from '../../stores/uiStore';
|
||||||
|
import { useChatStore } from '../../stores/chatStore';
|
||||||
|
import { useSpaceStore } from '../../stores/spaceStore';
|
||||||
|
import { useAuthStore } from '../../stores/authStore';
|
||||||
|
import { MessageList } from '../chat/MessageList';
|
||||||
|
import { MessageInput } from '../chat/MessageInput';
|
||||||
|
import { TypingIndicator } from '../chat/TypingIndicator';
|
||||||
|
|
||||||
|
interface MobileChatScreenProps {
|
||||||
|
params?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MobileChatScreen({ params }: MobileChatScreenProps) {
|
||||||
|
const popMobileScreen = useUIStore((s) => s.popMobileScreen);
|
||||||
|
const pushMobileScreen = useUIStore((s) => s.pushMobileScreen);
|
||||||
|
|
||||||
|
const channelId = params?.channelId;
|
||||||
|
const spaceId = params?.spaceId;
|
||||||
|
const isDm = spaceId === '@me';
|
||||||
|
|
||||||
|
const loadMessages = useChatStore((s) => s.loadMessages);
|
||||||
|
const setCurrentChannel = useChatStore((s) => s.setCurrentChannel);
|
||||||
|
const channels = useSpaceStore((s) => s.channels);
|
||||||
|
const dmChannels = useSpaceStore((s) => s.dmChannels);
|
||||||
|
const authUser = useAuthStore((s) => s.user);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!channelId) return;
|
||||||
|
setCurrentChannel(channelId);
|
||||||
|
loadMessages(channelId);
|
||||||
|
}, [channelId, setCurrentChannel, loadMessages]);
|
||||||
|
|
||||||
|
// Resolve channel/DM name
|
||||||
|
let channelName = 'Channel';
|
||||||
|
if (isDm && channelId) {
|
||||||
|
const dm = dmChannels.find(d => d.id === channelId);
|
||||||
|
if (dm) {
|
||||||
|
const otherMembers = dm.members.filter(m => m.id !== authUser?.id);
|
||||||
|
const isGroup = dm.members.length > 2;
|
||||||
|
channelName = isGroup
|
||||||
|
? otherMembers.map(m => m.displayName ?? m.username).join(', ')
|
||||||
|
: otherMembers[0]?.displayName ?? otherMembers[0]?.username ?? 'Direct Message';
|
||||||
|
}
|
||||||
|
} else if (!isDm && channelId) {
|
||||||
|
const ch = channels.find(c => c.id === channelId);
|
||||||
|
channelName = ch?.name || 'channel';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full bg-surface-chat">
|
||||||
|
{/* Header */}
|
||||||
|
<header className="h-12 flex items-center gap-2 px-3 border-b border-border-soft bg-surface-base shrink-0">
|
||||||
|
<button onClick={popMobileScreen} className="w-8 h-8 flex items-center justify-center text-txt-secondary hover:text-txt-primary">
|
||||||
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h1 className="text-sm font-semibold text-txt-primary truncate">
|
||||||
|
{isDm ? channelName : `# ${channelName}`}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
{!isDm && (
|
||||||
|
<button
|
||||||
|
onClick={() => pushMobileScreen('members')}
|
||||||
|
className="w-8 h-8 flex items-center justify-center text-txt-secondary hover:text-txt-primary"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Messages */}
|
||||||
|
<div className="flex-1 overflow-hidden">
|
||||||
|
<MessageList />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Typing indicator + Input */}
|
||||||
|
<TypingIndicator />
|
||||||
|
<MessageInput />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ import { MobileSpacesScreen } from './MobileSpacesScreen';
|
|||||||
// These will be replaced with real imports as each task is completed:
|
// These will be replaced with real imports as each task is completed:
|
||||||
const MobileDmsScreen = () => <PlaceholderScreen label="DMs" />;
|
const MobileDmsScreen = () => <PlaceholderScreen label="DMs" />;
|
||||||
const MobileYouScreen = () => <PlaceholderScreen label="You" />;
|
const MobileYouScreen = () => <PlaceholderScreen label="You" />;
|
||||||
const MobileChatScreen = ({ params: _params }: { params?: Record<string, string> }) => <PlaceholderScreen label="Chat" />;
|
import { MobileChatScreen } from './MobileChatScreen';
|
||||||
const MobileSettingsScreen = ({ initialPanel: _p }: { initialPanel?: string }) => <PlaceholderScreen label="Settings" />;
|
const MobileSettingsScreen = ({ initialPanel: _p }: { initialPanel?: string }) => <PlaceholderScreen label="Settings" />;
|
||||||
const MobileVoiceMiniBar = () => null;
|
const MobileVoiceMiniBar = () => null;
|
||||||
const MobileVoiceFullScreen = () => <PlaceholderScreen label="Voice" />;
|
const MobileVoiceFullScreen = () => <PlaceholderScreen label="Voice" />;
|
||||||
|
|||||||
Reference in New Issue
Block a user