refactor: rename Server → Space across entire codebase

Renames all domain terminology from "Server" to "Space" throughout the
application — database schema, API routes, shared types, stores, components,
and UI strings. Files renamed: ServerSidebar → SpaceSidebar, CreateServer →
CreateSpace, JoinServer → JoinSpace, ServerSettings → SpaceSettings,
serverStore → spaceStore, routes/servers → routes/spaces.
This commit is contained in:
Jannis Braun
2026-03-08 20:08:24 +01:00
parent d18d9c51f7
commit fc06e25731
62 changed files with 1342 additions and 1342 deletions
@@ -1,17 +1,17 @@
import React, { useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { ServerSidebar } from './ServerSidebar';
import { SpaceSidebar } from './SpaceSidebar';
import { ChannelSidebar } from './ChannelSidebar';
import { MainContent } from './MainContent';
import { RightPanel } from './RightPanel';
import { MobileNav } from './MobileNav';
import { ImagePreview } from '../chat/ImagePreview';
import { CreateServerModal } from '../modals/CreateServer';
import { JoinServerModal } from '../modals/JoinServer';
import { CreateSpaceModal } from '../modals/CreateSpace';
import { JoinSpaceModal } from '../modals/JoinSpace';
import { CreateChannelModal } from '../modals/CreateChannel';
import { InviteModal } from '../modals/InviteModal';
import { UserSettingsModal } from '../modals/UserSettings';
import { ServerSettingsModal } from '../modals/ServerSettings';
import { SpaceSettingsModal } from '../modals/SpaceSettings';
import { ChannelSettingsModal } from '../modals/ChannelSettingsModal';
import { NewDmModal } from '../modals/NewDmModal';
import { AddDmMemberModal } from '../modals/AddDmMemberModal';
@@ -25,14 +25,14 @@ import { useAuth } from '../../hooks/useAuth';
import { useWebSocket } from '../../hooks/useWebSocket';
import { useFederationToasts } from '../../hooks/useFederationToasts';
import { useLiveKit } from '../../hooks/useLiveKit';
import { useServerStore } from '../../stores/serverStore';
import { useSpaceStore } from '../../stores/spaceStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
import { useVoiceStore } from '../../stores/voiceStore';
import { AudioManager } from '../../audio/AudioManager';
export function AppLayout() {
const { serverId, channelId, inviteCode } = useParams<{ serverId?: string; channelId?: string; inviteCode?: string }>();
const { spaceId, channelId, inviteCode } = useParams<{ spaceId?: string; channelId?: string; inviteCode?: string }>();
const navigate = useNavigate();
// Global interaction handler to resume AudioContext
@@ -83,8 +83,8 @@ export function AppLayout() {
}, [outputDeviceId]);
const { user, isLoading } = useAuth();
const setCurrentServer = useServerStore((s) => s.setCurrentServer);
const loadServerDetail = useServerStore((s) => s.loadServerDetail);
const setCurrentSpace = useSpaceStore((s) => s.setCurrentSpace);
const loadSpaceDetail = useSpaceStore((s) => s.loadSpaceDetail);
const setCurrentChannel = useChatStore((s) => s.setCurrentChannel);
const loadMessages = useChatStore((s) => s.loadMessages);
const setIsMobile = useUIStore((s) => s.setIsMobile);
@@ -96,7 +96,7 @@ export function AppLayout() {
const userProfilePopout = useUIStore((s) => s.userProfilePopout);
const closeUserProfile = useUIStore((s) => s.closeUserProfile);
const channels = useServerStore((s) => s.channels);
const channels = useSpaceStore((s) => s.channels);
const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId);
const activeDmCall = useVoiceStore((s) => s.activeDmCall);
@@ -185,22 +185,22 @@ export function AppLayout() {
// Handle route params
useEffect(() => {
if (serverId === '@me') {
if (spaceId === '@me') {
setShowDms(true);
setCurrentServer(null);
} else if (serverId) {
setCurrentSpace(null);
} else if (spaceId) {
setShowDms(false);
setCurrentServer(serverId);
loadServerDetail(serverId);
setCurrentSpace(spaceId);
loadSpaceDetail(spaceId);
} else {
setShowDms(false);
setCurrentServer(null);
setCurrentSpace(null);
}
}, [serverId, setCurrentServer, loadServerDetail, setShowDms]);
}, [spaceId, setCurrentSpace, loadSpaceDetail, setShowDms]);
useEffect(() => {
if (inviteCode) {
openModal('joinServer');
openModal('joinSpace');
}
}, [inviteCode, openModal]);
@@ -208,35 +208,35 @@ export function AppLayout() {
if (channelId) {
setCurrentChannel(channelId);
loadMessages(channelId);
if (serverId && serverId !== '@me') {
useUIStore.getState().setLastChannel(serverId, channelId);
if (spaceId && spaceId !== '@me') {
useUIStore.getState().setLastChannel(spaceId, channelId);
}
} else {
setCurrentChannel(null);
}
}, [channelId, serverId, setCurrentChannel, loadMessages]);
}, [channelId, spaceId, setCurrentChannel, loadMessages]);
// Auto-select last visited (or first) channel when opening a server without a channelId
useEffect(() => {
if (!serverId || serverId === '@me' || channelId) return;
if (!spaceId || spaceId === '@me' || channelId) return;
if (channels.length === 0) return;
const firstChannel = channels[0];
if (!firstChannel) return;
// Guard: only redirect when channels belong to the target server.
// `channels` is shared per-view state set by loadServerDetail (async).
// `channels` is shared per-view state set by loadSpaceDetail (async).
// Without this check, stale channels from a previously viewed server
// would cause a wrong redirect during the fetch window.
const { channelToServerMap } = useServerStore.getState();
if (channelToServerMap.get(firstChannel.id) !== serverId) return;
const { channelToSpaceMap } = useSpaceStore.getState();
if (channelToSpaceMap.get(firstChannel.id) !== spaceId) return;
const lastId = useUIStore.getState().lastChannelPerServer[serverId];
const lastId = useUIStore.getState().lastChannelPerSpace[spaceId];
const target = (lastId && channels.find((c) => c.id === lastId)) || firstChannel;
if (target) {
navigate(`/channels/${serverId}/${target.id}`, { replace: true });
navigate(`/channels/${spaceId}/${target.id}`, { replace: true });
}
}, [serverId, channelId, channels, navigate]);
}, [spaceId, channelId, channels, navigate]);
if (isLoading || !user) {
return (
@@ -254,9 +254,9 @@ export function AppLayout() {
return (
<div className="h-screen flex flex-col md:grid md:grid-cols-[312px_1fr] md:grid-rows-[minmax(0,1fr)] bg-surface-base overflow-hidden">
{/* Server sidebar - always visible on desktop, toggled on mobile */}
{/* Space sidebar - always visible on desktop, toggled on mobile */}
<div className={`fixed inset-y-0 left-0 z-40 flex w-[312px] transition-transform duration-200 ${sidebarOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'} md:static md:z-auto md:w-auto md:transform-none`}>
<ServerSidebar />
<SpaceSidebar />
<ChannelSidebar />
</div>
@@ -267,12 +267,12 @@ export function AppLayout() {
</div>
{/* Modals */}
<CreateServerModal />
<JoinServerModal />
<CreateSpaceModal />
<JoinSpaceModal />
<CreateChannelModal />
<InviteModal />
<UserSettingsModal />
<ServerSettingsModal />
<SpaceSettingsModal />
<ChannelSettingsModal />
<NewDmModal />
<AddDmMemberModal />
@@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useServerStore, getChannelOrigin } from '../../stores/serverStore';
import { useSpaceStore, getChannelOrigin } from '../../stores/spaceStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
import { useAuthStore } from '../../stores/authStore';
@@ -18,16 +18,16 @@ import { parseFederatedUsername, isSelf } from '../../utils/identity';
import { joinVoiceChannel } from '../../utils/voice';
export function ChannelSidebar() {
const servers = useServerStore((s) => s.servers);
const currentServerId = useServerStore((s) => s.currentServerId);
const channels = useServerStore((s) => s.channels);
const dmChannels = useServerStore((s) => s.dmChannels);
const spaces = useSpaceStore((s) => s.spaces);
const currentSpaceId = useSpaceStore((s) => s.currentSpaceId);
const channels = useSpaceStore((s) => s.channels);
const dmChannels = useSpaceStore((s) => s.dmChannels);
const currentChannelId = useChatStore((s) => s.currentChannelId);
const setCurrentChannel = useChatStore((s) => s.setCurrentChannel);
const unreadChannels = useChatStore((s) => s.unreadChannels);
const openModal = useUIStore((s) => s.openModal);
const user = useAuthStore((s) => s.user);
const members = useServerStore((s) => s.members);
const members = useSpaceStore((s) => s.members);
const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId);
const activeDmCall = useVoiceStore((s) => s.activeDmCall);
const isMuted = useVoiceStore((s) => s.isMuted);
@@ -72,26 +72,26 @@ export function ChannelSidebar() {
}
};
const serverPermissions = useServerStore((s) => s.serverPermissions);
const server = servers.find(s => s.id === currentServerId);
const myServerPerms = currentServerId ? serverPermissions.get(currentServerId) : undefined;
const spacePermissions = useSpaceStore((s) => s.spacePermissions);
const space = spaces.find(s => s.id === currentSpaceId);
const mySpacePerms = currentSpaceId ? spacePermissions.get(currentSpaceId) : undefined;
const federationInstances = useInstanceStore((s) => s.instances);
const instanceLabel = useMemo(() => {
const origin = (server as any)?._instanceOrigin;
const origin = (space as any)?._instanceOrigin;
if (!origin) return null;
const inst = federationInstances.find(i => i.origin === origin);
if (inst) return inst.label;
try { return new URL(origin).host; } catch { return origin; }
}, [server, federationInstances]);
const canManageChannels = hasPermissionBit(myServerPerms, PermissionBits.MANAGE_CHANNELS);
}, [space, federationInstances]);
const canManageChannels = hasPermissionBit(mySpacePerms, PermissionBits.MANAGE_CHANNELS);
const textChannels = channels.filter(c => c.type === 'text');
const voiceChannels = channels.filter(c => c.type === 'voice' || c.type === 'video');
const handleChannelClick = (channelId: string) => {
setCurrentChannel(channelId);
navigate(`/channels/${currentServerId || '@me'}/${channelId}`);
navigate(`/channels/${currentSpaceId || '@me'}/${channelId}`);
};
const handleHomeClick = () => {
@@ -102,11 +102,11 @@ export function ChannelSidebar() {
const handleVoiceJoin = (channelId: string) => {
// Don't re-join the same channel — prevents duplicate LiveKit connections
if (currentVoiceChannelId === channelId) {
navigate(`/channels/${currentServerId}/${channelId}`);
navigate(`/channels/${currentSpaceId}/${channelId}`);
return;
}
joinVoiceChannel(channelId);
navigate(`/channels/${currentServerId}/${channelId}`);
navigate(`/channels/${currentSpaceId}/${channelId}`);
};
// Floating bottom panel — shared between DM view and server view
@@ -130,7 +130,7 @@ export function ChannelSidebar() {
</div>
) : null;
if (!server) {
if (!space) {
return (
<>
<div className="w-60 md:w-full bg-surface-channel flex flex-col flex-shrink-0 select-none md:pl-[72px] border-r border-border-hard">
@@ -260,7 +260,7 @@ export function ChannelSidebar() {
navigate('/channels/@me');
setCurrentChannel(null);
}
useServerStore.getState().closeDm(dm.id);
useSpaceStore.getState().closeDm(dm.id);
}}
className="opacity-0 group-hover:opacity-100 text-txt-tertiary hover:text-txt-primary transition-opacity flex-shrink-0 ml-1"
title="Close DM"
@@ -287,14 +287,14 @@ export function ChannelSidebar() {
return (
<>
<div className="w-60 md:w-full bg-surface-channel flex flex-col flex-shrink-0 select-none md:pl-[72px] border-r border-border-hard">
{/* Server header */}
{/* Space header */}
<div className="h-12 flex items-center border-b border-border-hard z-10 group/header">
<button
onClick={() => openModal('serverSettings')}
onClick={() => openModal('spaceSettings')}
className="flex-1 h-full px-4 flex items-center justify-between hover:bg-interactive-hover transition-colors min-w-0"
>
<div className="min-w-0">
<span className="font-bold text-[15px] tracking-[-0.02em] text-txt-primary truncate leading-tight block">{server.name}</span>
<span className="font-bold text-[15px] tracking-[-0.02em] text-txt-primary truncate leading-tight block">{space.name}</span>
{instanceLabel && (
<span className="text-[10px] text-txt-tertiary font-medium truncate block leading-tight">
{instanceLabel}
@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { useServerStore } from '../../stores/serverStore';
import { useSpaceStore } from '../../stores/spaceStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
import { useAuthStore } from '../../stores/authStore';
@@ -20,9 +20,9 @@ import { joinVoiceChannel } from '../../utils/voice';
export function MainContent() {
// 1. ALL HOOKS AT THE TOP
const channels = useServerStore((s) => s.channels);
const channels = useSpaceStore((s) => s.channels);
const currentChannelId = useChatStore((s) => s.currentChannelId);
const currentServerId = useServerStore((s) => s.currentServerId);
const currentSpaceId = useSpaceStore((s) => s.currentSpaceId);
const voiceChatOpen = useUIStore((s) => s.voiceChatOpen);
const voiceFullscreen = useUIStore((s) => s.voiceFullscreen);
const setVoiceFullscreen = useUIStore((s) => s.setVoiceFullscreen);
@@ -35,7 +35,7 @@ export function MainContent() {
const isExplorePage = location.pathname === '/explore';
const activeDmCall = useVoiceStore((s) => s.activeDmCall);
const outgoingCall = useVoiceStore((s) => s.outgoingCall);
const dmChannels = useServerStore((s) => s.dmChannels);
const dmChannels = useSpaceStore((s) => s.dmChannels);
const authUser = useAuthStore((s) => s.user);
const openModal = useUIStore((s) => s.openModal);
@@ -64,7 +64,7 @@ export function MainContent() {
const channel = channels.find(c => c.id === currentChannelId);
const isVoiceChannel = channel?.type === 'voice' || channel?.type === 'video';
if (showDms || isExplorePage || !currentServerId) {
if (showDms || isExplorePage || !currentSpaceId) {
if (!currentChannelId) {
if (isExplorePage) return <ExplorePage />;
return <FriendsPage />;
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import type { MemberWithUser } from '@backspace/shared';
import { useServerStore } from '../../stores/serverStore';
import { useSpaceStore } from '../../stores/spaceStore';
import { useUIStore } from '../../stores/uiStore';
import { Avatar } from '../ui/Avatar';
import { Username } from '../ui/Username';
@@ -42,14 +42,14 @@ function getMemberGroup(member: MemberWithUser, ownerId: string | undefined) {
}
export function MemberSidebar() {
const members = useServerStore((s) => s.members);
const servers = useServerStore((s) => s.servers);
const currentServerId = useServerStore((s) => s.currentServerId);
const members = useSpaceStore((s) => s.members);
const spaces = useSpaceStore((s) => s.spaces);
const currentSpaceId = useSpaceStore((s) => s.currentSpaceId);
const memberListOpen = useUIStore((s) => s.memberListOpen);
const openUserProfile = useUIStore((s) => s.openUserProfile);
const server = servers.find(s => s.id === currentServerId);
const ownerId = server?.ownerId;
const space = spaces.find(s => s.id === currentSpaceId);
const ownerId = space?.ownerId;
const { roleGroups, offlineMembers } = useMemo(() => {
const online = members.filter(m => m.user.status !== 'offline');
@@ -2,13 +2,13 @@ import React from 'react';
import { MemberSidebar } from './MemberSidebar';
import { ActivityPanel } from './ActivityPanel';
import { useUIStore } from '../../stores/uiStore';
import { useServerStore } from '../../stores/serverStore';
import { useSpaceStore } from '../../stores/spaceStore';
export function RightPanel() {
const showDms = useUIStore((s) => s.showDms);
const currentServerId = useServerStore((s) => s.currentServerId);
const currentSpaceId = useSpaceStore((s) => s.currentSpaceId);
if (showDms || !currentServerId) {
if (showDms || !currentSpaceId) {
return <ActivityPanel />;
}
@@ -1,11 +1,11 @@
import React, { useState, useMemo } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useServerStore } from '../../stores/serverStore';
import { useSpaceStore } from '../../stores/spaceStore';
import { useChatStore } from '../../stores/chatStore';
import { useUIStore } from '../../stores/uiStore';
import { useInstanceStore } from '../../stores/instanceStore';
import { getServerGradient, HOME_GRADIENT } from '../../utils/gradients';
import { getSpaceGradient, HOME_GRADIENT } from '../../utils/gradients';
interface SidebarItemProps {
id: string;
@@ -13,13 +13,13 @@ interface SidebarItemProps {
icon?: string | null;
active: boolean;
onClick: () => void;
type?: 'server' | 'dm' | 'action';
type?: 'space' | 'dm' | 'action';
actionType?: 'add' | 'join' | 'explore';
hasUnread?: boolean;
dimmed?: boolean;
}
function SidebarItem({ id, name, icon, active, onClick, type = 'server', actionType, hasUnread, dimmed }: SidebarItemProps) {
function SidebarItem({ id, name, icon, active, onClick, type = 'space', actionType, hasUnread, dimmed }: SidebarItemProps) {
const [isHovered, setIsHovered] = useState(false);
const firstLetter = name.charAt(0).toUpperCase();
@@ -41,11 +41,11 @@ function SidebarItem({ id, name, icon, active, onClick, type = 'server', actionT
return { background: HOME_GRADIENT.gradient };
}
// Server type — if it has a custom icon image, no gradient needed
// Space type — if it has a custom icon image, no gradient needed
if (icon) return undefined;
const serverGrad = getServerGradient(id, name);
return { background: serverGrad.gradient };
const spaceGrad = getSpaceGradient(id, name);
return { background: spaceGrad.gradient };
}, [type, id, name, icon, isHovered]);
const getButtonClasses = () => {
@@ -73,7 +73,7 @@ function SidebarItem({ id, name, icon, active, onClick, type = 'server', actionT
onMouseLeave={() => setIsHovered(false)}
>
{/* Pill Indicator */}
{(type === 'server' || type === 'dm') && (
{(type === 'space' || type === 'dm') && (
<div className="absolute -left-0 w-2 h-10 flex items-center">
<div
className={`bg-white rounded-r-full transition-all duration-200 origin-left ${getPillHeight()} w-1`}
@@ -112,12 +112,12 @@ function SidebarItem({ id, name, icon, active, onClick, type = 'server', actionT
);
}
export function ServerSidebar() {
const servers = useServerStore((s) => s.servers);
const currentServerId = useServerStore((s) => s.currentServerId);
const setCurrentServer = useServerStore((s) => s.setCurrentServer);
const channelToServerMap = useServerStore((s) => s.channelToServerMap);
const dmChannels = useServerStore((s) => s.dmChannels);
export function SpaceSidebar() {
const spaces = useSpaceStore((s) => s.spaces);
const currentSpaceId = useSpaceStore((s) => s.currentSpaceId);
const setCurrentSpace = useSpaceStore((s) => s.setCurrentSpace);
const channelToSpaceMap = useSpaceStore((s) => s.channelToSpaceMap);
const dmChannels = useSpaceStore((s) => s.dmChannels);
const showDms = useUIStore((s) => s.showDms);
const setShowDms = useUIStore((s) => s.setShowDms);
const openModal = useUIStore((s) => s.openModal);
@@ -128,11 +128,11 @@ export function ServerSidebar() {
const navigate = useNavigate();
const location = useLocation();
// Group servers by origin
const groupedServers = useMemo(() => {
const home = servers.filter(s => !(s as any)._instanceOrigin);
const remoteMap = new Map<string, typeof servers>();
for (const s of servers) {
// Group spaces by origin
const groupedSpaces = useMemo(() => {
const home = spaces.filter(s => !(s as any)._instanceOrigin);
const remoteMap = new Map<string, typeof spaces>();
for (const s of spaces) {
const origin = (s as any)._instanceOrigin;
if (!origin) continue;
const list = remoteMap.get(origin) || [];
@@ -140,7 +140,7 @@ export function ServerSidebar() {
remoteMap.set(origin, list);
}
return { home, remoteGroups: Array.from(remoteMap.entries()) };
}, [servers]);
}, [spaces]);
// Set of disconnected origins
const disconnectedOrigins = useMemo(() => {
@@ -153,15 +153,15 @@ export function ServerSidebar() {
return set;
}, [instances]);
// Compute which servers have unread channels
const unreadServerIds = useMemo(() => {
// Compute which spaces have unread channels
const unreadSpaceIds = useMemo(() => {
const ids = new Set<string>();
for (const channelId of unreadChannels) {
const serverId = channelToServerMap.get(channelId);
if (serverId) ids.add(serverId);
const spaceId = channelToSpaceMap.get(channelId);
if (spaceId) ids.add(spaceId);
}
return ids;
}, [unreadChannels, channelToServerMap]);
}, [unreadChannels, channelToSpaceMap]);
// Check if any DM channels are unread
const hasDmUnread = useMemo(() => {
@@ -171,28 +171,28 @@ export function ServerSidebar() {
return false;
}, [unreadChannels, dmChannels]);
const handleServerClick = (serverId: string) => {
const server = servers.find(s => s.id === serverId);
const origin = (server as any)?._instanceOrigin;
const handleSpaceClick = (spaceId: string) => {
const space = spaces.find(s => s.id === spaceId);
const origin = (space as any)?._instanceOrigin;
if (origin && disconnectedOrigins.has(origin)) {
const inst = instances.find(i => i.origin === origin);
addToast(`Reconnecting to ${inst?.label || 'remote instance'}...`, 'warning', 4000);
return;
}
setCurrentServer(serverId);
setCurrentSpace(spaceId);
setShowDms(false);
navigate(`/channels/${serverId}`);
navigate(`/channels/${spaceId}`);
};
const handleDmClick = () => {
setShowDms(true);
setCurrentServer(null);
setCurrentSpace(null);
setCurrentChannel(null);
navigate('/channels/@me');
};
const handleExploreClick = () => {
setCurrentServer(null);
setCurrentSpace(null);
setCurrentChannel(null);
navigate('/explore');
};
@@ -210,21 +210,21 @@ export function ServerSidebar() {
<div className="w-8 h-[2px] bg-interactive-muted rounded-full mb-1.5" />
{/* Home servers */}
{groupedServers.home.map((server) => (
{/* Home spaces */}
{groupedSpaces.home.map((space) => (
<SidebarItem
key={server.id}
id={server.id}
name={server.name}
icon={server.icon}
active={currentServerId === server.id}
onClick={() => handleServerClick(server.id)}
hasUnread={unreadServerIds.has(server.id)}
key={space.id}
id={space.id}
name={space.name}
icon={space.icon}
active={currentSpaceId === space.id}
onClick={() => handleSpaceClick(space.id)}
hasUnread={unreadSpaceIds.has(space.id)}
/>
))}
{/* Remote instance groups */}
{groupedServers.remoteGroups.map(([origin, groupServers]) => {
{groupedSpaces.remoteGroups.map(([origin, groupSpaces]) => {
const inst = instances.find(i => i.origin === origin);
const label = inst?.label || (() => { try { return new URL(origin).host; } catch { return '?'; } })();
const isDimmed = disconnectedOrigins.has(origin);
@@ -234,15 +234,15 @@ export function ServerSidebar() {
<div className="text-[9px] text-txt-tertiary font-medium uppercase tracking-wider mb-1 truncate max-w-[52px] text-center" title={label}>
{label}
</div>
{groupServers.map((server) => (
{groupSpaces.map((space) => (
<SidebarItem
key={server.id}
id={server.id}
name={server.name}
icon={server.icon}
active={currentServerId === server.id}
onClick={() => handleServerClick(server.id)}
hasUnread={unreadServerIds.has(server.id)}
key={space.id}
id={space.id}
name={space.name}
icon={space.icon}
active={currentSpaceId === space.id}
onClick={() => handleSpaceClick(space.id)}
hasUnread={unreadSpaceIds.has(space.id)}
dimmed={isDimmed}
/>
))}
@@ -253,26 +253,26 @@ export function ServerSidebar() {
<div className="w-8 h-[2px] bg-interactive-muted rounded-full mb-1.5" />
<SidebarItem
id="add-server"
name="Add a Server"
id="add-space"
name="Add a Space"
active={false}
onClick={() => openModal('createServer')}
onClick={() => openModal('createSpace')}
type="action"
actionType="add"
/>
<SidebarItem
id="join-server"
name="Join a Server"
id="join-space"
name="Join a Space"
active={false}
onClick={() => openModal('joinServer')}
onClick={() => openModal('joinSpace')}
type="action"
actionType="join"
/>
<SidebarItem
id="explore"
name="Explore Servers"
name="Explore Spaces"
active={location.pathname === '/explore'}
onClick={handleExploreClick}
type="action"