mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 03:55:38 -04:00
Refactor auth session handling and server actions across app
- Overhaul `lib/auth/server.ts` and `lib/auth/session.ts`; update all API routes and server actions to use the revised session pattern - Refactor server actions (settings, titles, watchlist, setup) for consistency with new auth layer - Extract `SetupForm` into its own client component with `useActionState`, animated steps, and copyable env snippets - Move landing page redirect logic into `app/page.tsx`; slim down `LandingPage` component - Add `proxy.ts` for local dev proxying - Minor cleanup to `NavBar`, `MobileTabBar`, and `TitleCard`
This commit is contained in:
@@ -3,22 +3,7 @@
|
||||
import { motion } from "motion/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { SofaLogo } from "@/components/sofa-logo";
|
||||
import { useSession } from "@/lib/auth/client";
|
||||
|
||||
/** Check whether the server has TMDB configured. */
|
||||
async function fetchSetupStatus(): Promise<boolean> {
|
||||
try {
|
||||
const res = await fetch("/api/setup/status");
|
||||
if (!res.ok) return true; // assume configured on error
|
||||
const data = await res.json();
|
||||
return !!data.tmdbConfigured;
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Poster positions arranged in angled columns behind the hero
|
||||
const posterLayout = [
|
||||
@@ -49,23 +34,6 @@ export function LandingPage({
|
||||
freshInstall: boolean;
|
||||
registrationOpen: boolean;
|
||||
}) {
|
||||
const { data: session, isPending } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (isPending) return;
|
||||
if (session?.user) {
|
||||
router.replace("/dashboard");
|
||||
return;
|
||||
}
|
||||
// If not logged in, check whether TMDB is configured
|
||||
fetchSetupStatus().then((configured) => {
|
||||
if (!configured) router.replace("/setup");
|
||||
});
|
||||
}, [session, isPending, router]);
|
||||
|
||||
if (isPending) return null;
|
||||
|
||||
return (
|
||||
<div className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden">
|
||||
{/* Background grain texture */}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { IconCompass, IconHome, IconSettings } from "@tabler/icons-react";
|
||||
import { motion } from "motion/react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useSession } from "@/lib/auth/client";
|
||||
|
||||
const tabs = [
|
||||
{ href: "/dashboard", label: "Home", icon: IconHome },
|
||||
@@ -13,11 +12,8 @@ const tabs = [
|
||||
] as const;
|
||||
|
||||
export function MobileTabBar() {
|
||||
const { data: session } = useSession();
|
||||
const pathname = usePathname();
|
||||
|
||||
if (!session?.user) return null;
|
||||
|
||||
return (
|
||||
<nav className="fixed bottom-0 left-0 right-0 z-50 border-t border-border/50 bg-background/90 pl-[env(safe-area-inset-left)] pr-[env(safe-area-inset-right)] backdrop-blur-xl sm:hidden">
|
||||
<div className="flex h-14 items-stretch">
|
||||
|
||||
@@ -14,15 +14,14 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { commandPaletteOpenAtom } from "@/lib/atoms/command-palette";
|
||||
import { signOut, useSession } from "@/lib/auth/client";
|
||||
import { signOut } from "@/lib/auth/client";
|
||||
|
||||
const navLinks = [
|
||||
{ href: "/dashboard", label: "Home" },
|
||||
{ href: "/explore", label: "Explore" },
|
||||
] as const;
|
||||
|
||||
export function NavBar() {
|
||||
const { data: session } = useSession();
|
||||
export function NavBar({ userName }: { userName: string }) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const setCommandPaletteOpen = useSetAtom(commandPaletteOpenAtom);
|
||||
@@ -96,7 +95,7 @@ export function NavBar() {
|
||||
render={<Link href="/settings" />}
|
||||
className="hidden items-center gap-1.5 rounded-md px-2 py-1.5 text-sm leading-none text-muted-foreground transition-colors hover:text-foreground sm:inline-flex"
|
||||
>
|
||||
{session?.user?.name}
|
||||
{userName}
|
||||
<IconSettings aria-hidden={true} className="size-3.5" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Settings</TooltipContent>
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { type MotionStyle, type MotionValue, motion } from "motion/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -83,8 +83,15 @@ function QuickAddButton({
|
||||
userStatus ?? null,
|
||||
);
|
||||
|
||||
const effectiveStatus = addedStatus;
|
||||
const config = effectiveStatus ? statusConfig[effectiveStatus] : null;
|
||||
// Sync local state when prop changes (e.g. after navigation or SWR revalidation)
|
||||
useEffect(() => {
|
||||
if (userStatus) {
|
||||
setState("added");
|
||||
setAddedStatus(userStatus);
|
||||
}
|
||||
}, [userStatus]);
|
||||
|
||||
const config = addedStatus ? statusConfig[addedStatus] : null;
|
||||
|
||||
async function handleClick(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
@@ -92,13 +99,9 @@ function QuickAddButton({
|
||||
if (state === "loading" || state === "added") return;
|
||||
setState("loading");
|
||||
try {
|
||||
const result = await quickAddToWatchlist(tmdbId, type);
|
||||
await quickAddToWatchlist(tmdbId, type);
|
||||
setState("added");
|
||||
setAddedStatus(result.alreadyAdded ? null : "watchlist");
|
||||
if (result.alreadyAdded) {
|
||||
// Already existed — keep as added but we don't know exact status
|
||||
setAddedStatus("watchlist");
|
||||
}
|
||||
setAddedStatus("watchlist");
|
||||
} catch {
|
||||
setState("idle");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user