diff --git a/packages/web/src/components/layout/AppLayout.tsx b/packages/web/src/components/layout/AppLayout.tsx
index 602ef643..075df99c 100644
--- a/packages/web/src/components/layout/AppLayout.tsx
+++ b/packages/web/src/components/layout/AppLayout.tsx
@@ -240,6 +240,20 @@ export function AppLayout() {
}
}, [spaceId, channelId, channels, navigate]);
+ // Guard: redirect when URL channelId no longer exists (deleted, permission revoked, etc.)
+ useEffect(() => {
+ if (!spaceId || spaceId === '@me' || !channelId) return;
+ if (channels.length === 0) return;
+
+ const { channelToSpaceMap } = useSpaceStore.getState();
+ const firstCh = channels[0];
+ if (!firstCh || channelToSpaceMap.get(firstCh.id) !== spaceId) return;
+
+ if (!channels.some(c => c.id === channelId)) {
+ navigate(`/channels/${spaceId}`, { replace: true });
+ }
+ }, [spaceId, channelId, channels, navigate]);
+
if (isLoading || !user) {
return (
diff --git a/packages/web/src/components/modals/ChannelSettingsModal.tsx b/packages/web/src/components/modals/ChannelSettingsModal.tsx
index 72cf828b..93ff5669 100644
--- a/packages/web/src/components/modals/ChannelSettingsModal.tsx
+++ b/packages/web/src/components/modals/ChannelSettingsModal.tsx
@@ -1,10 +1,8 @@
import React, { useState, useEffect } from 'react';
-import { useNavigate } from 'react-router-dom';
import { Modal } from '../ui/Modal';
import { ConfirmDialog } from '../ui/ConfirmDialog';
import { useUIStore } from '../../stores/uiStore';
import { useSpaceStore, getApiForOrigin } from '../../stores/spaceStore';
-import { useChatStore } from '../../stores/chatStore';
import { api } from '../../api/client';
import { PermissionBits, permissionsToString, stringToPermissions, hasPermissionBit } from '../../utils/permissions';
@@ -24,8 +22,6 @@ export function ChannelSettingsModal() {
const channels = useSpaceStore((s) => s.channels);
const spaces = useSpaceStore((s) => s.spaces);
const spacePermissions = useSpaceStore((s) => s.spacePermissions);
- const currentChannelId = useChatStore((s) => s.currentChannelId);
- const navigate = useNavigate();
const [isPrivate, setIsPrivate] = useState(false);
const [isLoading, setIsLoading] = useState(false);
@@ -116,9 +112,6 @@ export function ChannelSettingsModal() {
const channelApi = getApiForOrigin(space?._instanceOrigin ?? '');
await channelApi.channels.delete(channelId);
closeModal();
- if (currentChannelId === channelId) {
- navigate('/channels/@me');
- }
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to delete channel');
setIsDeleting(false);
diff --git a/packages/web/src/hooks/useWebSocket.ts b/packages/web/src/hooks/useWebSocket.ts
index 25aaa0b9..25d4f50c 100644
--- a/packages/web/src/hooks/useWebSocket.ts
+++ b/packages/web/src/hooks/useWebSocket.ts
@@ -633,18 +633,6 @@ function handleEvent(origin: string, event: ServerEvent): void {
useVoiceStore.setState({ voiceUsers: newVoiceUsers });
}
}
- {
- const { currentChannelId } = useChatStore.getState();
- if (currentChannelId === event.channelId) {
- const { channels: remainingChannels } = useSpaceStore.getState();
- const firstText = remainingChannels.find(c => c.type === 'text');
- if (firstText) {
- useChatStore.getState().setCurrentChannel(firstText.id);
- } else {
- useChatStore.getState().setCurrentChannel(null);
- }
- }
- }
break;
}