- React + TanStack Router + shadcn/ui SPA under spa/ - serve spa/dist at /app/ with index.html fallback for client routing - Dockerfile: node build stage for SPA, copy dist into runtime image - README: document SPA, CORS_ORIGINS env var, architecture entry - vite base set to /app/, manifest.json paths fixed
66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
import { createFileRoute, Link } from "@tanstack/react-router"
|
|
import { useTranslation } from "react-i18next"
|
|
import { ArrowLeft, UserCheck, UserPlus } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { ProfileView, ProfileSkeleton } from "@/components/profile-view"
|
|
import { useAuth } from "@/components/auth-provider"
|
|
import { useUserProfile } from "@/hooks/use-users"
|
|
import { useFollow, useUnfollow, useFollowing } from "@/hooks/use-social"
|
|
|
|
export const Route = createFileRoute("/_app/users/$id")({
|
|
component: UserProfilePage,
|
|
})
|
|
|
|
function UserProfilePage() {
|
|
const { t } = useTranslation()
|
|
const { id } = Route.useParams()
|
|
const { auth } = useAuth()
|
|
const { data, isPending } = useUserProfile(id, { view: "trends" })
|
|
const { data: followingData } = useFollowing()
|
|
const followMutation = useFollow()
|
|
const unfollowMutation = useUnfollow()
|
|
|
|
if (isPending) return <ProfileSkeleton />
|
|
if (!data) return null
|
|
|
|
const isSelf = auth?.user_id === id
|
|
const isFollowing = followingData?.actors.some((a) => a.handle === data.username) ?? false
|
|
|
|
return (
|
|
<div className="p-4">
|
|
<Link to="/" className="mb-4 inline-flex items-center gap-1 text-sm text-muted-foreground">
|
|
<ArrowLeft className="size-4" /> {t("common.back")}
|
|
</Link>
|
|
|
|
<ProfileView
|
|
data={data}
|
|
userId={id}
|
|
headerRight={
|
|
!isSelf ? (
|
|
isFollowing ? (
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => unfollowMutation.mutate({ handle: data.username })}
|
|
disabled={unfollowMutation.isPending}
|
|
>
|
|
<UserCheck className="mr-1 size-3.5" />
|
|
{t("common.following")}
|
|
</Button>
|
|
) : (
|
|
<Button
|
|
size="sm"
|
|
onClick={() => followMutation.mutate({ handle: data.username })}
|
|
disabled={followMutation.isPending}
|
|
>
|
|
<UserPlus className="mr-1 size-3.5" />
|
|
{t("common.follow")}
|
|
</Button>
|
|
)
|
|
) : undefined
|
|
}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|