Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions src/assets/icons/ghost.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { ComponentPropsWithoutRef } from "react";
import { twMerge } from "tailwind-merge";

import Ghost from "@/assets/icons/ghost.svg?react";

export interface AvatarProps extends Omit<ComponentPropsWithoutRef<"img">, "src"> {
src?: string | null;
}

export default function Avatar({ src, alt = "사용자 아바타", className, ...props }: AvatarProps) {
return (
<div
className={twMerge(
"flex aspect-square h-max items-center justify-center overflow-hidden rounded-full bg-gray-50",
className
)}
>
{src ? (
<img src={src} alt={alt} className="h-full w-full object-cover" {...props} />
) : (
<Ghost className="text-primary h-3/4 w-3/4" />
)}
</div>
);
}
9 changes: 3 additions & 6 deletions src/features/friends/list/List.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Home, UserMinus2 } from "lucide-react";
import { useEffect, useState } from "react";

import Avatar from "@/components/Avatar/Avatar";
import Button from "@/components/Button/Button";
import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import { useAuthStore } from "@/stores/useAuthStore";
Expand Down Expand Up @@ -62,12 +63,8 @@ export default function List() {
return (
<li key={friend.id}>
<div className="flex items-center gap-3 p-2 pl-2 select-none">
<div className="shrink-0">
<img
src={profile.avatar_url ?? ""}
alt="프로필 아바타"
className="h-8 w-8 rounded-full object-cover"
/>
<div className="h-8 w-8 shrink-0">
<Avatar src={profile.avatar_url} />
</div>
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-sm font-medium">{profile.nickname}</span>
Expand Down
10 changes: 3 additions & 7 deletions src/features/friends/request/Request.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react";

import Avatar from "@/components/Avatar/Avatar";
import Button from "@/components/Button/Button";
import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import { useAuthStore } from "@/stores/useAuthStore";
Expand Down Expand Up @@ -59,13 +60,8 @@ export default function Request() {
{requests.map((request) => (
<li key={request.id}>
<div className="flex items-center gap-3 p-2 pl-2 select-none">
<div className="shrink-0">
<img
// TODO: 디폴트 아바타 설정
src={request.requester.avatar_url ?? ""}
alt="프로필 아바타"
className="h-8 w-8 rounded-full object-cover"
/>
<div className="h-8 w-8 shrink-0">
<Avatar src={request.requester.avatar_url} />
</div>
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-sm font-medium">
Expand Down
10 changes: 3 additions & 7 deletions src/features/friends/search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react";

import Avatar from "@/components/Avatar/Avatar";
import Button from "@/components/Button/Button";
import BevelScrollContainer from "@/components/Container/BevelScrollContainer";
import Input from "@/components/Input/Input";
Expand Down Expand Up @@ -75,13 +76,8 @@ export default function Search() {
{results.map((Profile) => (
<li key={Profile.auth_id}>
<div className="flex items-center gap-3 p-2 pl-2 select-none">
<div className="shrink-0">
<img
// TODO: 디폴트 아바타 설정
src={Profile.avatar_url!}
alt="프로필 아바타"
className="h-8 w-8 rounded-full object-cover"
/>
<div className="h-8 w-8 shrink-0">
<Avatar src={Profile.avatar_url} />
</div>
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-sm font-medium">{Profile.nickname}</span>
Expand Down
12 changes: 2 additions & 10 deletions src/features/minihome/gallery/GalleryDetailPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ChevronLeft, Heart, Send, X } from "lucide-react";
import { useEffect, useState } from "react";
import { twMerge } from "tailwind-merge";

import Avatar from "@/components/Avatar/Avatar";
import Button from "@/components/Button/Button";
import Input from "@/components/Input/Input";
import { useAuthStore } from "@/stores/useAuthStore";
Expand Down Expand Up @@ -304,22 +305,13 @@ export default function GalleryDetailPanel({
comments.map((comment) => {
const nickname = comment.author?.nickname ?? "알 수 없음";
const avatar = comment.author?.avatar_url;
const fallbackInitial = nickname.charAt(0);
const body = stringifyCommentContent(comment.content);

return (
<div key={comment.id} className="bevel-default bg-text-invert p-3">
<div className="flex items-start gap-3">
<div className="bevel-default bg-surface text-primary flex h-10 w-10 items-center justify-center overflow-hidden">
{avatar ? (
<img
src={avatar}
alt={`${nickname} avatar`}
className="h-full w-full object-cover"
/>
) : (
fallbackInitial
)}
<Avatar src={avatar} />
</div>
<div className="flex-1">
<div className="text-muted mb-1 flex items-center justify-between">
Expand Down
18 changes: 5 additions & 13 deletions src/features/minihome/home/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import dayjs from "dayjs";
import { MessageCircle, UserRound, Star } from "lucide-react";
import { MessageCircle, Star } from "lucide-react";
import { useEffect, useState, type Dispatch, type SetStateAction } from "react";
import { twMerge } from "tailwind-merge";

import Avatar from "@/components/Avatar/Avatar";
import { useAuthStore } from "@/stores/useAuthStore";
import type { Database } from "@/types/database.types";
import type { MiniHomeTabs } from "@/types/minihome.types";
Expand Down Expand Up @@ -205,21 +206,12 @@ export default function HomePage({
};

return (
<div className="flex w-[592px] p-3">
<div className="flex p-3">
{/* 왼쪽 프로필 영역 */}
<div className="mr-3 flex w-1/3 flex-col items-center">
{/* 프로필 이미지 */}
<div className="relative">
{avatarUrl ? (
<div className="h-32 w-32 overflow-hidden rounded-full border-4 border-[#B2AAEB]">
<img className="h-full w-full object-cover" src={avatarUrl} alt="" />
</div>
) : (
<div className="bevel-default flex h-32 w-32 items-center justify-center rounded-full">
<UserRound size={48} color="#B2AAEB" />
</div>
)}
</div>

<Avatar src={avatarUrl} />

{/* 이름 */}
<p className="mt-4 text-lg text-[#342b4e]">{nickname}</p>
Expand Down
11 changes: 3 additions & 8 deletions src/features/minihome/post/detail/CommentItem.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import dayjs from "dayjs";
import "dayjs/locale/ko";
import relativeTime from "dayjs/plugin/relativeTime";
import { Trash, User } from "lucide-react";
import { Trash } from "lucide-react";

import Avatar from "@/components/Avatar/Avatar";
import { useAuthUser } from "@/hooks/useAuthUser";
import type { CommentWithProfile } from "@/types/post.types";

Expand All @@ -24,13 +25,7 @@ export default function CommentItem({ comment, onDelete, isMyHome }: CommentItem
return (
<div className="flex gap-2 border-b border-gray-100 p-2 last:border-none">
<div className="h-7 w-7 shrink-0 overflow-hidden bg-gray-200">
{avatarUrl ? (
<img src={avatarUrl} alt={authorName} className="h-full w-full object-cover" />
) : (
<div className="flex h-full w-full items-center justify-center text-gray-500">
<User size={16} />
</div>
)}
<Avatar src={avatarUrl} />
</div>

<div className="flex flex-1 flex-col gap-1">
Expand Down