fix(mobile): resolve all TypeScript errors from integration
- Fix voiceStore method names (toggleMic, leaveVoice, setCurrentVoiceChannel) - Fix MemberWithUser nested user property access - Pass required channelId props to MessageList, TypingIndicator, MessageInput - Fix ConfirmDialog props (isOpen, description, variant, onClose) - Add mobileStyle prop to Modal component (fullscreen + sheet modes) - Fix fromId/isAdmin camelCase property names - Guard possibly undefined touch events and array accesses - Fix notification channelId resolution
This commit is contained in:
@@ -35,7 +35,7 @@ export function MobileBottomNav() {
|
||||
const hasUnreadSpaces = Array.from(unreadChannels).some(chId => !voiceChannelIds.has(chId));
|
||||
|
||||
// Pending friend requests — filter for incoming only
|
||||
const pendingIncoming = requests.filter(r => r.status === 'pending' && r.from_id !== authUser?.id);
|
||||
const pendingIncoming = requests.filter(r => r.status === 'pending' && r.fromId !== authUser?.id);
|
||||
|
||||
const handleTab = (tab: 'spaces' | 'dms' | 'you') => {
|
||||
setMobileTab(tab);
|
||||
|
||||
@@ -75,12 +75,12 @@ export function MobileChatScreen({ params }: MobileChatScreenProps) {
|
||||
|
||||
{/* Messages */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<MessageList />
|
||||
{channelId && <MessageList channelId={channelId} />}
|
||||
</div>
|
||||
|
||||
{/* Typing indicator + Input */}
|
||||
<TypingIndicator />
|
||||
<MessageInput />
|
||||
{channelId && <TypingIndicator channelId={channelId} />}
|
||||
{channelId && <MessageInput channelId={channelId} channelName={channelName} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ const sectionIcons: Record<string, React.ReactNode> = {
|
||||
export function MobileSettingsScreen({ initialPanel }: MobileSettingsScreenProps) {
|
||||
const popMobileScreen = useUIStore((s) => s.popMobileScreen);
|
||||
const pushMobileScreen = useUIStore((s) => s.pushMobileScreen);
|
||||
const isAdmin = useAuthStore((s) => s.user?.is_admin);
|
||||
const isAdmin = useAuthStore((s) => s.user?.isAdmin);
|
||||
|
||||
// If initialPanel is set, render that panel directly
|
||||
if (initialPanel) {
|
||||
|
||||
@@ -63,7 +63,8 @@ export function MobileShell() {
|
||||
const path = location.pathname;
|
||||
const match = path.match(/^\/channels\/([^/]+)\/([^/]+)$/);
|
||||
if (match && mobileStack.length === 0) {
|
||||
const [, spaceId, channelId] = match;
|
||||
const spaceId = match[1] ?? '';
|
||||
const channelId = match[2] ?? '';
|
||||
if (spaceId === '@me') {
|
||||
pushMobileScreen('channel-chat', { channelId, spaceId: '@me' });
|
||||
} else {
|
||||
|
||||
@@ -27,7 +27,7 @@ export function MobileSpacesScreen() {
|
||||
|
||||
const voiceUsers = useVoiceStore((s) => s.voiceUsers);
|
||||
const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId);
|
||||
const joinChannel = useVoiceStore((s) => s.joinChannel);
|
||||
const setCurrentVoiceChannel = useVoiceStore((s) => s.setCurrentVoiceChannel);
|
||||
|
||||
const pushMobileScreen = useUIStore((s) => s.pushMobileScreen);
|
||||
const setMobileTab = useUIStore((s) => s.setMobileTab);
|
||||
@@ -52,7 +52,7 @@ export function MobileSpacesScreen() {
|
||||
|
||||
// Auto-select first space if none selected
|
||||
useEffect(() => {
|
||||
if (!selectedSpaceId && spaces.length > 0) {
|
||||
if (!selectedSpaceId && spaces.length > 0 && spaces[0]) {
|
||||
setSelectedSpaceId(spaces[0].id);
|
||||
}
|
||||
}, [selectedSpaceId, spaces]);
|
||||
@@ -107,7 +107,7 @@ export function MobileSpacesScreen() {
|
||||
|
||||
const handleChannelTap = (channel: Channel) => {
|
||||
if (channel.type === 'voice') {
|
||||
joinChannel(channel.id);
|
||||
setCurrentVoiceChannel(channel.id);
|
||||
return;
|
||||
}
|
||||
if (selectedSpaceId) {
|
||||
|
||||
@@ -13,11 +13,11 @@ export function MobileVoiceFullScreen() {
|
||||
const isDeafened = useVoiceStore((s) => s.isDeafened);
|
||||
const isCameraOn = useVoiceStore((s) => s.isCameraOn);
|
||||
const isScreenSharing = useVoiceStore((s) => s.isScreenSharing);
|
||||
const toggleMute = useVoiceStore((s) => s.toggleMute);
|
||||
const toggleMute = useVoiceStore((s) => s.toggleMic);
|
||||
const toggleDeafen = useVoiceStore((s) => s.toggleDeafen);
|
||||
const toggleCamera = useVoiceStore((s) => s.toggleCamera);
|
||||
const toggleScreenShare = useVoiceStore((s) => s.toggleScreenShare);
|
||||
const leaveChannel = useVoiceStore((s) => s.leaveChannel);
|
||||
const leaveVoice = useVoiceStore((s) => s.leaveVoice);
|
||||
const voiceUsers = useVoiceStore((s) => s.voiceUsers);
|
||||
const voiceUserStates = useVoiceStore((s) => s.voiceUserStates);
|
||||
|
||||
@@ -62,9 +62,9 @@ export function MobileVoiceFullScreen() {
|
||||
const member = members.find(m => m.userId === userId);
|
||||
if (member) {
|
||||
return {
|
||||
name: member.nickname ?? member.displayName ?? member.username,
|
||||
avatar: member.avatar ? `/api/uploads/${member.avatar}` : null,
|
||||
avatarColor: member.avatarColor,
|
||||
name: member.nickname ?? member.user?.displayName ?? member.user?.username ?? 'User',
|
||||
avatar: member.user?.avatar ? `/api/uploads/${member.user.avatar}` : null,
|
||||
avatarColor: member.user?.avatarColor ?? null,
|
||||
};
|
||||
}
|
||||
// Check DM members
|
||||
@@ -82,7 +82,7 @@ export function MobileVoiceFullScreen() {
|
||||
};
|
||||
|
||||
const handleDisconnect = () => {
|
||||
leaveChannel();
|
||||
leaveVoice();
|
||||
popMobileScreen();
|
||||
};
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ export function MobileVoiceMiniBar() {
|
||||
const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId);
|
||||
const isMuted = useVoiceStore((s) => s.isMuted);
|
||||
const isDeafened = useVoiceStore((s) => s.isDeafened);
|
||||
const toggleMute = useVoiceStore((s) => s.toggleMute);
|
||||
const toggleMute = useVoiceStore((s) => s.toggleMic);
|
||||
const toggleDeafen = useVoiceStore((s) => s.toggleDeafen);
|
||||
const voiceUsers = useVoiceStore((s) => s.voiceUsers);
|
||||
const leaveChannel = useVoiceStore((s) => s.leaveChannel);
|
||||
const leaveVoice = useVoiceStore((s) => s.leaveVoice);
|
||||
|
||||
const channels = useSpaceStore((s) => s.channels);
|
||||
const dmChannels = useSpaceStore((s) => s.dmChannels);
|
||||
@@ -21,7 +21,8 @@ export function MobileVoiceMiniBar() {
|
||||
if (!currentVoiceChannelId) return null;
|
||||
|
||||
// Don't show mini-bar if voice full-screen is on top of the stack
|
||||
const topScreen = mobileStack.length > 0 ? mobileStack[mobileStack.length - 1].screen : null;
|
||||
const topEntry = mobileStack.length > 0 ? mobileStack[mobileStack.length - 1] : undefined;
|
||||
const topScreen = topEntry?.screen ?? null;
|
||||
if (topScreen === 'voice-full') return null;
|
||||
|
||||
// Resolve channel name
|
||||
@@ -91,7 +92,7 @@ export function MobileVoiceMiniBar() {
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); leaveChannel(); }}
|
||||
onClick={(e) => { e.stopPropagation(); leaveVoice(); }}
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center bg-accent-rose/20 text-accent-rose hover:bg-accent-rose/30 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
|
||||
@@ -142,12 +142,13 @@ export function MobileYouScreen() {
|
||||
|
||||
{showLogoutConfirm && (
|
||||
<ConfirmDialog
|
||||
isOpen={true}
|
||||
title="Log Out"
|
||||
message="Are you sure you want to log out?"
|
||||
description="Are you sure you want to log out?"
|
||||
confirmLabel="Log Out"
|
||||
onConfirm={() => { setShowLogoutConfirm(false); logout(); }}
|
||||
onCancel={() => setShowLogoutConfirm(false)}
|
||||
destructive
|
||||
onClose={() => setShowLogoutConfirm(false)}
|
||||
variant="danger"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user