Skip to content

Commit 3b0c594

Browse files
committed
Add SWR for faster UX
1 parent 2c70f14 commit 3b0c594

File tree

7 files changed

+122
-137
lines changed

7 files changed

+122
-137
lines changed

package-lock.json

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"remixicon-react": "^1.0.0",
3434
"socket.io": "^4.8.1",
3535
"socket.io-client": "^4.8.1",
36+
"swr": "^2.3.6",
3637
"vm2": "^3.9.19",
3738
"vscode-icons-js": "^11.6.1"
3839
},

src/app/(auth)/login/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default function LoginPage() {
4343
if (result?.ok && !result.error) {
4444
router.push("/rooms");
4545
} else if (result?.error === "CredentialsSignin") {
46-
toast.error("Invalid email or password");
46+
toast.error("Invalid email or password");
4747
} else {
4848
toast.error(result?.error || "Something went wrong");
4949
}

src/app/(auth)/signup/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default function SignupPage() {
4646
router.push("/login");
4747
} else {
4848
const data = await res.json();
49-
toast.error(data.error || "Signup failed");
49+
toast.error(data.error || "Signup failed");
5050
}
5151
};
5252

src/app/rooms/page.tsx

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"use client";
22

3-
import { useEffect, useState } from "react";
3+
import { useState } from "react";
44
import { useRouter } from "next/navigation";
5+
import useSWR, { mutate } from "swr";
56
import {
67
ChevronDown,
78
DoorClosed,
@@ -26,11 +27,16 @@ interface Invite {
2627
invitedBy: { id: string; name: string | null };
2728
}
2829

30+
const fetcher = (url: string) =>
31+
fetch(url, { credentials: "include" }).then((res) => {
32+
if (!res.ok) throw new Error("Failed to fetch");
33+
return res.json();
34+
});
35+
2936
export default function RoomsPage() {
3037
const { data: session, status } = useSession();
3138
const router = useRouter();
3239

33-
const [rooms, setRooms] = useState<Room[]>([]);
3440
const [roomName, setRoomName] = useState("");
3541
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
3642
const [selectedRoomToDelete, setSelectedRoomToDelete] = useState<Room | null>(
@@ -39,51 +45,37 @@ export default function RoomsPage() {
3945
const [selectedRoomSlug, setSelectedRoomSlug] = useState("");
4046
const [showInviteModal, setShowInviteModal] = useState(false);
4147
const [inviteEmail, setInviteEmail] = useState("");
42-
const [pendingInvites, setPendingInvites] = useState<Invite[]>([]);
4348
const [loading, setLoading] = useState(false);
4449
const [roomToLeave, setRoomToLeave] = useState<Room | null>(null);
4550

46-
const loadRoomsAndInvites = async () => {
47-
try {
48-
const [roomRes, inviteRes] = await Promise.all([
49-
fetch("/api/rooms", { credentials: "include" }),
50-
fetch("api/invite", { credentials: "include" }),
51-
]);
52-
53-
const roomsData = await roomRes.json();
54-
const invitesData = await inviteRes.json();
55-
56-
setRooms(Array.isArray(roomsData) ? roomsData : []);
57-
setPendingInvites(Array.isArray(invitesData) ? invitesData : []);
58-
} catch (error) {
59-
toast.error("Failed to load rooms or invites");
60-
setRooms([]);
61-
setPendingInvites([]);
62-
}
63-
};
64-
65-
useEffect(() => {
66-
if (status === "unauthenticated") {
67-
router.push("/login");
68-
}
69-
}, [status]);
51+
const {
52+
data: rooms = [],
53+
isLoading: roomsLoading,
54+
error: roomsError,
55+
} = useSWR<Room[]>(status === "authenticated" ? "/api/rooms" : null, fetcher);
56+
57+
const {
58+
data: pendingInvites = [],
59+
isLoading: invitesLoading,
60+
error: invitesError,
61+
} = useSWR<Invite[]>(
62+
status === "authenticated" ? "/api/invite" : null,
63+
fetcher
64+
);
7065

71-
useEffect(() => {
72-
loadRoomsAndInvites();
73-
}, []);
66+
if (status === "unauthenticated") {
67+
router.push("/login");
68+
}
7469

75-
useEffect(() => {
76-
if (!showInviteModal) {
77-
loadRoomsAndInvites();
78-
}
79-
}, [showInviteModal]);
70+
if (status === "loading" || roomsLoading || invitesLoading) {
71+
return <Loader />;
72+
}
8073

8174
const sendInvite = async () => {
8275
if (!selectedRoomSlug) {
8376
toast.error("Please select a room to invite to");
8477
return;
8578
}
86-
8779
if (!inviteEmail.trim()) {
8880
toast.error("Please enter an email to invite");
8981
return;
@@ -107,7 +99,8 @@ export default function RoomsPage() {
10799
toast.success("Invite sent successfully");
108100
setInviteEmail("");
109101
setSelectedRoomSlug("");
110-
loadRoomsAndInvites();
102+
103+
mutate("/api/invite");
111104
} catch (error: any) {
112105
toast.error(error.message || "Something went wrong while sending invite");
113106
}
@@ -124,7 +117,8 @@ export default function RoomsPage() {
124117
body: JSON.stringify({ action }),
125118
});
126119

127-
loadRoomsAndInvites();
120+
mutate("/api/rooms");
121+
mutate("/api/invite");
128122
} catch (err) {
129123
console.error(err);
130124
toast.error("Failed to respond to invite");
@@ -150,9 +144,11 @@ export default function RoomsPage() {
150144

151145
if (res.ok) {
152146
const newRoom = await res.json();
153-
setRooms((prev) => [...prev, { ...newRoom, owned: true }]);
154147
setRoomName("");
155148
toast.success("Room created");
149+
150+
mutate("/api/rooms");
151+
156152
router.push(`/room/${newRoom.slug}`);
157153
} else {
158154
const error = await res.json();
@@ -173,8 +169,8 @@ export default function RoomsPage() {
173169
});
174170

175171
if (res.ok) {
176-
setRooms((prev) => prev.filter((room) => room.slug !== slug));
177172
toast.success("Room deleted successfully");
173+
mutate("/api/rooms");
178174
} else {
179175
const error = await res.json();
180176
toast.error(error.error || "Failed to delete room");
@@ -192,8 +188,8 @@ export default function RoomsPage() {
192188
});
193189

194190
if (res.ok) {
195-
setRooms((prev) => prev.filter((room) => room.slug !== slug));
196-
toast.success("Room leaved successfully");
191+
toast.success("Room left successfully");
192+
mutate("/api/rooms");
197193
} else {
198194
const error = await res.json();
199195
toast.error(error.error || "Failed to leave room");
@@ -204,10 +200,6 @@ export default function RoomsPage() {
204200
}
205201
};
206202

207-
if (status === "loading") {
208-
return <Loader />;
209-
}
210-
211203
return (
212204
<div className="min-h-screen w-full bg-gradient-to-br from-black via-[#111111] to-gray-900 text-white">
213205
<HomeNavbar />

0 commit comments

Comments
 (0)