Skip to content

Commit

Permalink
プロフィール画面でアバター画像を変更可能に (#152)
Browse files Browse the repository at this point in the history
* [frontend] Add avatar upload form
  • Loading branch information
usatie authored Dec 20, 2023
1 parent b71073b commit 33479a9
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 19 deletions.
23 changes: 22 additions & 1 deletion frontend/app/lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { cookies } from "next/headers";
import { redirect, RedirectType } from "next/navigation";
import { revalidatePath } from "next/cache";
import { destroySession } from "./session";
import { destroySession, getCurrentUserId } from "./session";
import type { User } from "@/app/ui/user/card";

export async function signOut() {
Expand Down Expand Up @@ -256,3 +256,24 @@ export async function signInAsTestUser() {
await signIn({ email, password });
redirect("/");
}

export async function uploadAvatar(formData: FormData) {
const userId = await getCurrentUserId();
const payload = new FormData();
payload.append("avatar", formData.get("avatar") as Blob);
const res = await fetch(`${process.env.API_URL}/user/${userId}/avatar`, {
method: "POST",
headers: {
Authorization: "Bearer " + getAccessToken(),
},
body: payload,
});
const data = await res.json();
if (!res.ok) {
console.error("uploadAvatar error: ", data);
return "Error";
} else {
revalidatePath("/profile");
return "Success";
}
}
74 changes: 56 additions & 18 deletions frontend/app/ui/profile/profile-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import { Input } from "@/components/ui/input";
import { Stack } from "@/app/ui/layout/stack";
import { Label } from "@/components/ui/label";
import { useAuthContext } from "@/app/lib/client-auth";

function AvatarSkeleton() {
return <Skeleton className="rounded-full h-20 w-20" />;
}
import { useRef } from "react";
import { uploadAvatar } from "@/app/lib/actions";

function ProfileItem({ type, title, value }: ProfileItemProps) {
return (
Expand All @@ -28,25 +26,65 @@ export type ProfileItemProps = {
value?: string;
};

function Avatar({ avatarURL }: { avatarURL?: string }) {
if (avatarURL) {
return (
<img
src={avatarURL}
className="rounded-full w-20 h-20 object-cover"
alt="Avatar"
/>
);
} else {
return <Skeleton className="rounded-full h-20 w-20" />;
}
}

function AvatarForm({ avatarURL }: { avatarURL?: string }) {
const inputRef = useRef<HTMLInputElement>(null);
const submitAvatarForm = () => {
const form = document.getElementById("avatar-form") as HTMLFormElement;
form.requestSubmit();
};
return (
<form id="avatar-form" action={uploadAvatar}>
<button type="button" onClick={() => inputRef.current?.click()}>
<Avatar avatarURL={avatarURL} />
</button>
<input
ref={inputRef}
accept="image/*"
aria-label="Change Avatar"
name="avatar"
hidden
type="file"
onChange={submitAvatarForm}
/>
</form>
);
}

export default function ProfileForm() {
const { currentUser } = useAuthContext();
// Menu: min 100px
// Profile : the rest
return (
<form>
<Stack spacing={4}>
<Stack spacing={1}>
<div className="text-2xl">Profile</div>
<Separator />
</Stack>
<AvatarSkeleton />
<ProfileItem type="text" title="name" value={currentUser?.name} />
<ProfileItem type="email" title="email" value={currentUser?.email} />
<div></div>
<Button variant="outline" className="w-40">
Save
</Button>
<Stack spacing={4}>
<Stack spacing={1}>
<div className="text-2xl">Profile</div>
<Separator />
</Stack>
</form>
<AvatarForm avatarURL={currentUser?.avatarURL} />
<form>
<Stack spacing={4}>
<ProfileItem type="text" title="name" value={currentUser?.name} />
<ProfileItem type="email" title="email" value={currentUser?.email} />
<div></div>
<Button variant="outline" className="w-40">
Save
</Button>
</Stack>
</form>
</Stack>
);
}

0 comments on commit 33479a9

Please sign in to comment.