feat: enhance room management and UI with join/leave functionality
- Added GraphQL mutations and subscriptions for joining and leaving rooms - Implemented room member tracking and display in room list - Added leave room confirmation and error handling in chat room - Updated room list and chat room components with new interaction features - Improved UI with member badges, join/leave buttons, and error messages - Enhanced room query to include member information
This commit is contained in:
parent
16731409df
commit
c737258aed
6 changed files with 339 additions and 49 deletions
|
@ -1,6 +1,6 @@
|
|||
import { createSignal, createEffect, For, Show } from 'solid-js';
|
||||
import { gql } from '@urql/core';
|
||||
import { createQuery, createSubscription } from '@urql/solid';
|
||||
import { createQuery, createSubscription, createMutation } from '@urql/solid';
|
||||
import { Room } from '../types';
|
||||
|
||||
const ROOMS_QUERY = gql`
|
||||
|
@ -11,6 +11,9 @@ const ROOMS_QUERY = gql`
|
|||
description
|
||||
isPrivate
|
||||
createdAt
|
||||
members {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -23,6 +26,36 @@ const ROOM_ADDED_SUBSCRIPTION = gql`
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -34,10 +67,15 @@ interface RoomListProps {
|
|||
|
||||
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
|
||||
|
@ -45,6 +83,14 @@ export function RoomList(props: RoomListProps) {
|
|||
query: ROOM_ADDED_SUBSCRIPTION,
|
||||
});
|
||||
|
||||
// Subscribe to room updates (when members change)
|
||||
const [roomUpdatedSubscription] = createSubscription({
|
||||
query: ROOM_UPDATED_SUBSCRIPTION,
|
||||
});
|
||||
|
||||
// Join room mutation
|
||||
const [joinRoomResult, joinRoom] = createMutation(JOIN_ROOM_MUTATION);
|
||||
|
||||
// Load initial rooms
|
||||
createEffect(() => {
|
||||
const result = roomsQuery;
|
||||
|
@ -68,18 +114,93 @@ export function RoomList(props: RoomListProps) {
|
|||
}
|
||||
});
|
||||
|
||||
// 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 = 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 ${props.selectedRoomId === room.id ? 'selected' : ''}`}
|
||||
onClick={() => props.onSelectRoom(room.id)}
|
||||
>
|
||||
<div class='room-name'>{room.name}</div>
|
||||
<div class='room-description'>{room.description}</div>
|
||||
<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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue