Some checks failed
CI / test (push) Failing after 43s
- 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.
216 lines
5.1 KiB
TypeScript
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>
|
|
);
|
|
}
|