Redesign WatchLink application UI
This commit is contained in:
@@ -40,6 +40,7 @@ export async function sendFriendRequest(formData: FormData) {
|
||||
}
|
||||
|
||||
revalidatePath("/friends");
|
||||
revalidatePath("/people");
|
||||
revalidatePath("/dashboard");
|
||||
}
|
||||
|
||||
@@ -66,5 +67,6 @@ async function updateIncomingRequest(formData: FormData, status: "ACCEPTED" | "D
|
||||
});
|
||||
|
||||
revalidatePath("/friends");
|
||||
revalidatePath("/people");
|
||||
revalidatePath("/dashboard");
|
||||
}
|
||||
|
||||
53
src/lib/room-actions.ts
Normal file
53
src/lib/room-actions.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
"use server";
|
||||
|
||||
import { RoomVisibility } from "@prisma/client";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { prisma } from "./prisma";
|
||||
import { requireCurrentUser } from "./session";
|
||||
|
||||
function normalizeSlug(value: string) {
|
||||
return value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9_-]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.slice(0, 48);
|
||||
}
|
||||
|
||||
export async function createRoom(formData: FormData) {
|
||||
const user = await requireCurrentUser();
|
||||
const name = String(formData.get("name") || "").trim();
|
||||
const visibility = String(formData.get("visibility") || "FRIENDS");
|
||||
const baseSlug = normalizeSlug(name);
|
||||
|
||||
if (!name || !baseSlug) {
|
||||
redirect("/rooms?error=invalid-room");
|
||||
}
|
||||
|
||||
let slug = baseSlug;
|
||||
let suffix = 2;
|
||||
while (await prisma.room.findUnique({ where: { slug }, select: { id: true } })) {
|
||||
slug = `${baseSlug}-${suffix}`;
|
||||
suffix += 1;
|
||||
}
|
||||
|
||||
const allowedVisibility: RoomVisibility[] = ["PUBLIC", "FRIENDS", "EXPLICIT", "ROLE_RESTRICTED"];
|
||||
const roomVisibility = allowedVisibility.includes(visibility as RoomVisibility)
|
||||
? (visibility as RoomVisibility)
|
||||
: "FRIENDS";
|
||||
|
||||
const room = await prisma.room.create({
|
||||
data: {
|
||||
name,
|
||||
slug,
|
||||
ownerId: user.id,
|
||||
visibility: roomVisibility
|
||||
},
|
||||
select: { slug: true }
|
||||
});
|
||||
|
||||
revalidatePath("/rooms");
|
||||
revalidatePath("/dashboard");
|
||||
redirect(`/rooms/${encodeURIComponent(room.slug)}`);
|
||||
}
|
||||
36
src/lib/shell.ts
Normal file
36
src/lib/shell.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { prisma } from "./prisma";
|
||||
import { getCurrentUser, userIsAdmin } from "./session";
|
||||
|
||||
type CurrentUser = NonNullable<Awaited<ReturnType<typeof getCurrentUser>>>;
|
||||
|
||||
export async function getShellContext(user: CurrentUser) {
|
||||
const [rooms, pendingRequests, activeRoomCount] = await Promise.all([
|
||||
prisma.room.findMany({
|
||||
where: {
|
||||
OR: [{ ownerId: user.id }, { members: { some: { userId: user.id } } }, { visibility: "PUBLIC" }]
|
||||
},
|
||||
include: { _count: { select: { members: true, mediaSources: true } } },
|
||||
orderBy: [{ ownerId: "desc" }, { updatedAt: "desc" }],
|
||||
take: 8
|
||||
}),
|
||||
prisma.friendship.count({ where: { receiverId: user.id, status: "PENDING" } }),
|
||||
prisma.room.count({ where: { mediaSources: { some: {} } } })
|
||||
]);
|
||||
|
||||
return {
|
||||
isAdmin: userIsAdmin(user),
|
||||
userName: user.displayName || user.username,
|
||||
pendingRequests,
|
||||
activeRoomCount,
|
||||
rooms: rooms.map((room) => ({
|
||||
id: room.id,
|
||||
name: room.name,
|
||||
slug: room.slug,
|
||||
href: `/rooms/${encodeURIComponent(room.slug)}`,
|
||||
visibility: room.visibility,
|
||||
participantCount: room._count.members + 1,
|
||||
queueCount: room._count.mediaSources,
|
||||
isPersonal: room.ownerId === user.id
|
||||
}))
|
||||
};
|
||||
}
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
import { compare, hash } from "bcryptjs";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Prisma, type User } from "@prisma/client";
|
||||
import { prisma } from "./prisma";
|
||||
import { setSession } from "./session";
|
||||
import { clearSession, setSession } from "./session";
|
||||
|
||||
function normalizeUsername(value: FormDataEntryValue | null) {
|
||||
return String(value || "")
|
||||
@@ -17,24 +18,33 @@ export async function registerUser(formData: FormData) {
|
||||
const password = String(formData.get("password") || "");
|
||||
|
||||
if (!username || password.length < 10) {
|
||||
throw new Error("Username is required and password must be at least 10 characters.");
|
||||
redirect("/register?error=invalid");
|
||||
}
|
||||
|
||||
const passwordHash = await hash(password, 12);
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
displayName: username,
|
||||
passwordHash,
|
||||
ownedRooms: {
|
||||
create: {
|
||||
slug: `@${username}`,
|
||||
name: `${username}'s room`,
|
||||
visibility: "FRIENDS"
|
||||
let user: User;
|
||||
|
||||
try {
|
||||
user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
displayName: username,
|
||||
passwordHash,
|
||||
ownedRooms: {
|
||||
create: {
|
||||
slug: `@${username}`,
|
||||
name: `${username}'s room`,
|
||||
visibility: "FRIENDS"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
|
||||
redirect("/register?error=username");
|
||||
}
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
await setSession(user.id);
|
||||
redirect("/dashboard");
|
||||
@@ -46,14 +56,19 @@ export async function loginUser(formData: FormData) {
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { username } });
|
||||
if (!user) {
|
||||
throw new Error("Invalid username or password.");
|
||||
redirect("/login?error=credentials");
|
||||
}
|
||||
|
||||
const ok = await compare(password, user.passwordHash);
|
||||
if (!ok) {
|
||||
throw new Error("Invalid username or password.");
|
||||
redirect("/login?error=credentials");
|
||||
}
|
||||
|
||||
await setSession(user.id);
|
||||
redirect("/dashboard");
|
||||
}
|
||||
|
||||
export async function logoutUser() {
|
||||
await clearSession();
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user