unreal-chat/apps/web/src/components/room-list.tsx
Juan Sebastian Montoya 857ffcd6b4
Some checks failed
CI / test (push) Failing after 43s
chore: add ESLint configuration for api and web packages
- Introduced ESLint configuration files for both api and web packages to enforce coding standards.
- Updated MinioService and TokenService to improve error handling and type definitions.
- Refactored localStorage access in web components to use window.localStorage for consistency.
- Enhanced ESLint rules in base configuration to improve code quality and maintainability.
2025-05-06 01:16:16 -05:00

216 lines
5.1 KiB
TypeScript

import { createSignal, createEffect, For, Show } from 'solid-js';
import { gql } from '@urql/core';
import { createQuery, createSubscription, createMutation } from '@urql/solid';
import { Room } from '../types';
const ROOMS_QUERY = gql`
query GetRooms {
rooms {
id
name
description
isPrivate
createdAt
members {
id
}
}
}
`;
const ROOM_ADDED_SUBSCRIPTION = gql`
subscription OnRoomAdded {
roomAdded {
id
name
description
isPrivate
createdAt
members {
id
}
}
}
`;
const ROOM_UPDATED_SUBSCRIPTION = gql`
subscription OnRoomUpdated {
roomUpdated {
id
name
description
isPrivate
createdAt
members {
id
}
}
}
`;
const JOIN_ROOM_MUTATION = gql`
mutation JoinRoom($roomId: ID!) {
joinRoom(roomId: $roomId) {
id
name
members {
id
}
}
}
`;
interface RoomListProps {
onSelectRoom: (roomId: string) => void;
selectedRoomId?: string;
}
export function RoomList(props: RoomListProps) {
const [rooms, setRooms] = createSignal<Room[]>([]);
const [error, setError] = createSignal<string | null>(null);
const [isJoining, setIsJoining] = createSignal(false);
// Query rooms
const [roomsQuery] = createQuery({
query: ROOMS_QUERY,
context: {
requestPolicy: 'network-only',
},
});
// Subscribe to new rooms
const [roomAddedSubscription] = createSubscription<{
roomAdded: Room;
}>({
query: ROOM_ADDED_SUBSCRIPTION,
});
// Subscribe to room updates (when members change)
const [roomUpdatedSubscription] = createSubscription<{
roomUpdated: Room;
}>({
query: ROOM_UPDATED_SUBSCRIPTION,
});
// Join room mutation
const [joinRoomResult, joinRoom] = createMutation<{
joinRoom: Room;
}>(JOIN_ROOM_MUTATION);
// Load initial rooms
createEffect(() => {
const result = roomsQuery;
if (result.data?.rooms) {
setRooms(result.data.rooms);
}
});
// Handle new rooms from subscription
createEffect(() => {
const result = roomAddedSubscription;
if (result.data?.roomAdded) {
const newRoom = result.data.roomAdded;
setRooms((prev) => {
// Check if room already exists
if (prev.some((room) => room.id === newRoom.id)) {
return prev;
}
return [...prev, newRoom];
});
}
});
// Handle room updates from subscription
createEffect(() => {
const result = roomUpdatedSubscription;
if (result.data?.roomUpdated) {
const updatedRoom = result.data.roomUpdated;
setRooms((prev) => {
return prev.map((room) => {
if (room.id === updatedRoom.id) {
return { ...room, ...updatedRoom };
}
return room;
});
});
}
});
// Handle join room error
createEffect(() => {
if (joinRoomResult.error) {
setError(joinRoomResult.error.message);
setIsJoining(false);
}
});
const handleJoinRoom = async (roomId: string, event: MouseEvent) => {
event.stopPropagation();
setError(null);
setIsJoining(true);
try {
const result = await joinRoom({ roomId });
if (result.data?.joinRoom) {
// Update the local room data with the joined room
setRooms((prev) =>
prev.map((room) =>
room.id === roomId
? { ...room, members: result.data!.joinRoom.members }
: room
)
);
// Select the room after joining
props.onSelectRoom(roomId);
}
} catch (err) {
console.error('Failed to join room:', err);
} finally {
setIsJoining(false);
}
};
// Check if the current user is a member of a room
const isMember = (room: Room) => {
const userId = window.localStorage.getItem('userId');
return room.members?.some((member) => member.id === userId);
};
return (
<div class='room-list'>
<h2>Chat Rooms</h2>
<Show when={error()}>
<div class='error-message'>{error()}</div>
</Show>
<Show when={!roomsQuery.fetching} fallback={<div>Loading rooms...</div>}>
<For each={rooms()}>
{(room) => (
<div class='room-item'>
<div
class={`room-info ${props.selectedRoomId === room.id ? 'selected' : ''}`}
onClick={() =>
isMember(room) ? props.onSelectRoom(room.id) : null
}
>
<div class='room-name'>
{room.name}
{isMember(room) && <span class='member-badge'>Member</span>}
</div>
<div class='room-description'>{room.description}</div>
</div>
<Show when={!isMember(room)}>
<button
class='join-button'
onClick={(e) => handleJoinRoom(room.id, e)}
disabled={isJoining()}
>
{isJoining() ? 'Joining...' : 'Join'}
</button>
</Show>
</div>
)}
</For>
</Show>
</div>
);
}