mirror of
https://github.com/Ategon/Jamjar.git
synced 2025-02-12 06:16:21 +00:00
Compare commits
No commits in common. "a5c8ba0e34f245b86745d8fe3039a50eaf446c06" and "d980d11be049fa88b5adc95ae32591399d04fcc5" have entirely different histories.
a5c8ba0e34
...
d980d11be0
6 changed files with 29 additions and 128 deletions
|
@ -5,7 +5,10 @@ const nextConfig: NextConfig = {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
hostname: "**",
|
hostname: "static-cdn.jtvnw.net",
|
||||||
|
port: "",
|
||||||
|
pathname: "/**",
|
||||||
|
search: "",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,25 +1,17 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Editor from "@/components/editor";
|
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
import { getCookie, hasCookie } from "@/helpers/cookie";
|
import { getCookie, hasCookie } from "@/helpers/cookie";
|
||||||
import { UserType } from "@/types/UserType";
|
import { UserType } from "@/types/UserType";
|
||||||
import { Avatar, Button, Form, Input } from "@nextui-org/react";
|
import { Button, Form, Input } from "@nextui-org/react";
|
||||||
import { redirect, usePathname } from "next/navigation";
|
import { redirect, usePathname } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { LoaderCircle } from "lucide-react";
|
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
export default function UserPage() {
|
export default function UserPage() {
|
||||||
const [user, setUser] = useState<UserType>();
|
const [user, setUser] = useState<UserType>();
|
||||||
const [profilePicture, setProfilePicture] = useState("");
|
const [profilePicture, setProfilePicture] = useState("");
|
||||||
const [name, setName] = useState("");
|
|
||||||
const [bannerPicture, setBannerPicture] = useState("");
|
|
||||||
const [bio, setBio] = useState("");
|
|
||||||
const [errors] = useState({});
|
const [errors] = useState({});
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [waitingSave, setWaitingSave] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUser();
|
loadUser();
|
||||||
|
@ -45,9 +37,6 @@ export default function UserPage() {
|
||||||
setUser(data);
|
setUser(data);
|
||||||
|
|
||||||
setProfilePicture(data.profilePicture ?? "");
|
setProfilePicture(data.profilePicture ?? "");
|
||||||
setBannerPicture(data.bannerPicture ?? "");
|
|
||||||
setBio(data.bio ?? "");
|
|
||||||
setName(data.name ?? "");
|
|
||||||
} else {
|
} else {
|
||||||
setUser(undefined);
|
setUser(undefined);
|
||||||
}
|
}
|
||||||
|
@ -55,31 +44,19 @@ export default function UserPage() {
|
||||||
}, [pathname]);
|
}, [pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center">
|
<div className="absolute flex items-center justify-center top-0 left-0 w-screen h-screen">
|
||||||
{!user ? (
|
{!user ? (
|
||||||
"Loading settings..."
|
"Loading settings..."
|
||||||
) : (
|
) : (
|
||||||
<Form
|
<Form
|
||||||
className="w-full max-w-2xl flex flex-col gap-4"
|
className="w-full max-w-xs flex flex-col gap-4"
|
||||||
validationErrors={errors}
|
validationErrors={errors}
|
||||||
onReset={() => {
|
onReset={() => {
|
||||||
setProfilePicture(user.profilePicture ?? "");
|
setProfilePicture(user.profilePicture ?? "");
|
||||||
setBannerPicture(user.bannerPicture ?? "");
|
|
||||||
setBio(user.bio ?? "");
|
|
||||||
setName(user.name ?? "");
|
|
||||||
}}
|
}}
|
||||||
onSubmit={async (e) => {
|
onSubmit={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const sanitizedBio = sanitizeHtml(bio);
|
|
||||||
|
|
||||||
if (!name) {
|
|
||||||
toast.error("You need to enter a name");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setWaitingSave(true);
|
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
process.env.NEXT_PUBLIC_MODE === "PROD"
|
process.env.NEXT_PUBLIC_MODE === "PROD"
|
||||||
? "https://d2jam.com/api/v1/user"
|
? "https://d2jam.com/api/v1/user"
|
||||||
|
@ -87,10 +64,7 @@ export default function UserPage() {
|
||||||
{
|
{
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
slug: user.slug,
|
slug: user.slug,
|
||||||
name: name,
|
|
||||||
bio: sanitizedBio,
|
|
||||||
profilePicture: profilePicture,
|
profilePicture: profilePicture,
|
||||||
bannerPicture: bannerPicture,
|
|
||||||
}),
|
}),
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -103,28 +77,11 @@ export default function UserPage() {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
toast.success("Changed settings");
|
toast.success("Changed settings");
|
||||||
setUser(await response.json());
|
setUser(await response.json());
|
||||||
setWaitingSave(false);
|
|
||||||
} else {
|
} else {
|
||||||
toast.error("Failed to update settings");
|
toast.error("Failed to update settings");
|
||||||
setWaitingSave(false);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p className="text-3xl">Settings</p>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Name"
|
|
||||||
labelPlacement="outside"
|
|
||||||
name="name"
|
|
||||||
placeholder="Enter a name"
|
|
||||||
type="text"
|
|
||||||
value={name}
|
|
||||||
onValueChange={setName}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p>Bio</p>
|
|
||||||
<Editor content={bio} setContent={setBio} />
|
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
label="Profile Picture"
|
label="Profile Picture"
|
||||||
labelPlacement="outside"
|
labelPlacement="outside"
|
||||||
|
@ -135,38 +92,9 @@ export default function UserPage() {
|
||||||
onValueChange={setProfilePicture}
|
onValueChange={setProfilePicture}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{profilePicture && <Avatar src={profilePicture} />}
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Banner Picture"
|
|
||||||
labelPlacement="outside"
|
|
||||||
name="bannerPicture"
|
|
||||||
placeholder="Enter a url to an image"
|
|
||||||
type="text"
|
|
||||||
value={bannerPicture}
|
|
||||||
onValueChange={setBannerPicture}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{bannerPicture &&
|
|
||||||
bannerPicture.startsWith("https://") &&
|
|
||||||
bannerPicture.length > 8 && (
|
|
||||||
<div className="bg-[#222222] h-28 w-full relative">
|
|
||||||
<Image
|
|
||||||
src={bannerPicture}
|
|
||||||
alt={`${user.name}'s profile banner`}
|
|
||||||
className="object-cover"
|
|
||||||
fill
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button color="primary" type="submit">
|
<Button color="primary" type="submit">
|
||||||
{waitingSave ? (
|
Save
|
||||||
<LoaderCircle className="animate-spin" size={16} />
|
|
||||||
) : (
|
|
||||||
<p>Save</p>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="reset" variant="flat">
|
<Button type="reset" variant="flat">
|
||||||
Reset
|
Reset
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { UserType } from "@/types/UserType";
|
import { UserType } from "@/types/UserType";
|
||||||
import { Avatar } from "@nextui-org/react";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
@ -26,31 +24,8 @@ export default function UserPage() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{user && (
|
{user && (
|
||||||
<div className="border-2 border-[#222224] relative rounded-xl overflow-hidden bg-[#18181a]">
|
<div>
|
||||||
<div className="bg-[#222222] h-28 relative">
|
<p>{user.name}</p>
|
||||||
{user.bannerPicture && (
|
|
||||||
<Image
|
|
||||||
src={user.bannerPicture}
|
|
||||||
alt={`${user.name}'s profile banner`}
|
|
||||||
className="object-cover"
|
|
||||||
fill
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Avatar
|
|
||||||
className="absolute rounded-full left-16 top-16 h-24 w-24 bg-transparent"
|
|
||||||
src={user.profilePicture}
|
|
||||||
/>
|
|
||||||
<div className="p-8 mt-8">
|
|
||||||
<p className="text-3xl">{user.name}</p>
|
|
||||||
<div
|
|
||||||
className="prose dark:prose-invert !duration-250 !ease-linear !transition-all"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html:
|
|
||||||
user.bio && user.bio != "<p></p>" ? user.bio : "No user bio",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownSection,
|
|
||||||
DropdownTrigger,
|
DropdownTrigger,
|
||||||
NavbarItem,
|
NavbarItem,
|
||||||
} from "@nextui-org/react";
|
} from "@nextui-org/react";
|
||||||
|
@ -27,23 +26,21 @@ export default function PCNavbarUser({ user }: NavbarUserProps) {
|
||||||
/>
|
/>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownSection title={user.name}>
|
<DropdownItem
|
||||||
<DropdownItem
|
key="profile"
|
||||||
key="profile"
|
className="text-[#333] dark:text-white"
|
||||||
className="text-[#333] dark:text-white"
|
href={`/u/${user.slug}`}
|
||||||
href={`/u/${user.slug}`}
|
>
|
||||||
>
|
Profile
|
||||||
Profile
|
</DropdownItem>
|
||||||
</DropdownItem>
|
<DropdownItem
|
||||||
<DropdownItem
|
showDivider
|
||||||
showDivider
|
key="settings"
|
||||||
key="settings"
|
className="text-[#333] dark:text-white"
|
||||||
className="text-[#333] dark:text-white"
|
href="/settings"
|
||||||
href="/settings"
|
>
|
||||||
>
|
Settings
|
||||||
Settings
|
</DropdownItem>
|
||||||
</DropdownItem>
|
|
||||||
</DropdownSection>
|
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
key="logout"
|
key="logout"
|
||||||
color="danger"
|
color="danger"
|
||||||
|
|
|
@ -181,14 +181,14 @@ export default function Posts() {
|
||||||
? `https://d2jam.com/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
? `https://d2jam.com/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
||||||
tagRules
|
tagRules
|
||||||
? Object.entries(tagRules)
|
? Object.entries(tagRules)
|
||||||
.map((key) => `${key}`)
|
.map((key, value) => `${key}-${value}`)
|
||||||
.join("_")
|
.join("_")
|
||||||
: ""
|
: ""
|
||||||
}`
|
}`
|
||||||
: `http://localhost:3005/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
: `http://localhost:3005/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
||||||
tagRules
|
tagRules
|
||||||
? Object.entries(tagRules)
|
? Object.entries(tagRules)
|
||||||
.map((key) => `${key}`)
|
.map((key, value) => `${key}-${value}`)
|
||||||
.join("_")
|
.join("_")
|
||||||
: ""
|
: ""
|
||||||
}`
|
}`
|
||||||
|
@ -201,14 +201,14 @@ export default function Posts() {
|
||||||
? `https://d2jam.com/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
? `https://d2jam.com/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
||||||
tagRules
|
tagRules
|
||||||
? Object.entries(tagRules)
|
? Object.entries(tagRules)
|
||||||
.map((key) => `${key}`)
|
.map((key, value) => `${key}-${value}`)
|
||||||
.join("_")
|
.join("_")
|
||||||
: ""
|
: ""
|
||||||
}&sticky=true`
|
}&sticky=true`
|
||||||
: `http://localhost:3005/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
: `http://localhost:3005/api/v1/posts?sort=${sort}&time=${time}&tags=${
|
||||||
tagRules
|
tagRules
|
||||||
? Object.entries(tagRules)
|
? Object.entries(tagRules)
|
||||||
.map((key) => `${key}`)
|
.map((key, value) => `${key}-${value}`)
|
||||||
.join("_")
|
.join("_")
|
||||||
: ""
|
: ""
|
||||||
}&sticky=true`
|
}&sticky=true`
|
||||||
|
|
|
@ -2,9 +2,7 @@ export interface UserType {
|
||||||
id: number;
|
id: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
name: string;
|
name: string;
|
||||||
bio: string;
|
|
||||||
profilePicture: string;
|
profilePicture: string;
|
||||||
bannerPicture: string;
|
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
mod: boolean;
|
mod: boolean;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
|
|
Loading…
Reference in a new issue