From df29fb3d7cbb03b08fa1ed96db89fca98f5fae77 Mon Sep 17 00:00:00 2001 From: Hee Su Date: Thu, 11 Apr 2024 15:16:30 +0900 Subject: [PATCH] =?UTF-8?q?Feature:=20Footer=20prefetch=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94,=20=EC=B1=84=ED=8C=85=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=2080%=20(#686)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 일부 이미지 최적화 * feat: ChatList 퍼블리싱, formatDate 함수 모듈화 * refactor: 이미지 최적화, Footer의 페이지들 prefetch * refactor: 최적화 사항 베포를 위한 chat 퍼블리싱 주석 --- packages/web/next.config.mjs | 4 + packages/web/public/sprite/sprite.svg | 1276 +++++++++-------- .../community/components/ArticleItem.tsx | 25 +- .../[articleId]/components/ArticleItem.tsx | 8 +- .../chatList/components/ChatCardList.tsx | 23 + .../grouping/chatList/components/ChatItem.tsx | 64 + .../chatList/components/ChatListHeader.tsx | 11 +- .../grouping/chatList/components/dummy.ts | 44 + .../[lng]/(main)/grouping/chatList/page.tsx | 17 +- .../grouping/detail/[groupId]/chat/page.tsx | 3 - .../[groupId]/components/GroupDetail.tsx | 28 +- .../components/GroupDetailHeader.tsx | 4 +- .../[groupId]/components/chat/ChatContent.tsx | 29 + .../(main)/grouping/detail/[groupId]/page.tsx | 4 +- .../components/ProfileDetailSection.tsx | 2 +- .../src/app/i18n/locales/ko/groupDetail.json | 1 + packages/web/src/components/Avatar/Avatar.tsx | 12 +- .../web/src/components/Card/GroupingCard.tsx | 3 +- packages/web/src/components/Footer/Footer.tsx | 5 +- .../web/src/components/Form/MessageForm.tsx | 62 + .../web/src/components/Modal/BottomSheet.tsx | 1 + .../web/src/components/Modal/ImageModal.tsx | 2 +- packages/web/src/utils/formatDate.ts | 14 + packages/web/src/utils/formatMeetingDate.ts | 3 +- 24 files changed, 977 insertions(+), 668 deletions(-) create mode 100644 packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatCardList.tsx create mode 100644 packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatItem.tsx create mode 100644 packages/web/src/app/[lng]/(main)/grouping/chatList/components/dummy.ts delete mode 100644 packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/chat/page.tsx create mode 100644 packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/chat/ChatContent.tsx create mode 100644 packages/web/src/components/Form/MessageForm.tsx create mode 100644 packages/web/src/utils/formatDate.ts diff --git a/packages/web/next.config.mjs b/packages/web/next.config.mjs index 308362ab5..5963aab24 100644 --- a/packages/web/next.config.mjs +++ b/packages/web/next.config.mjs @@ -20,6 +20,10 @@ const nextConfig = { hostname: 'opendata.mofa.go.kr', }, ], + deviceSizes: [450], + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384, 450], + minimumCacheTTL: 31536000, + formats: ['image/webp'], }, }; export default withBundleAnalyzer(withPlaiceholder(nextConfig)); diff --git a/packages/web/public/sprite/sprite.svg b/packages/web/public/sprite/sprite.svg index 36cf74875..57a9a808c 100644 --- a/packages/web/public/sprite/sprite.svg +++ b/packages/web/public/sprite/sprite.svg @@ -1,603 +1,673 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/web/src/app/[lng]/(main)/community/components/ArticleItem.tsx b/packages/web/src/app/[lng]/(main)/community/components/ArticleItem.tsx index e8eaae9d0..1b0ab9c47 100644 --- a/packages/web/src/app/[lng]/(main)/community/components/ArticleItem.tsx +++ b/packages/web/src/app/[lng]/(main)/community/components/ArticleItem.tsx @@ -1,7 +1,6 @@ 'use client'; -import { format, formatDistanceToNow } from 'date-fns'; -import { enUS, ko } from 'date-fns/locale'; +import { ko } from 'date-fns/locale'; import Image from 'next/image'; import { CommunityArticle } from '@/apis/community'; @@ -14,19 +13,7 @@ import { Flex } from '@/components/Layout'; import { Spacing } from '@/components/Spacing'; import useAppRouter from '@/hooks/useAppRouter'; import cn from '@/utils/cn'; - -const formatDate = (date: string, locale: Locale) => { - const d = new Date(date); - const now = Date.now(); - const diff = (now - d.getTime()) / 1000; - if (diff < 60 * 1) { - return '방금 전'; - } - if (diff < 60 * 60 * 24 * 3) { - return formatDistanceToNow(d, { addSuffix: true, locale }); - } - return format(d, 'MM/dd', { locale }); -}; +import { formatDate } from '@/utils/formatDate'; interface ArticleItemProps { articleData: CommunityArticle; @@ -34,7 +21,7 @@ interface ArticleItemProps { } export default function ArticleItem({ articleData, onClick }: ArticleItemProps) { - const { t, i18n } = useTranslation('community'); + const { t } = useTranslation('community'); const { push } = useAppRouter(); const { article, writer } = articleData; @@ -52,8 +39,6 @@ export default function ArticleItem({ articleData, onClick }: ArticleItemProps) const { isCertifiedStudent, reliabilityLevel, nickName, countryImage, profileImage } = writer; - const locale = i18n.language === 'ko' ? ko : enUS; - return (
{t(`category.${category.name}`)} -

{formatDate(createdAt, locale)}

+

{formatDate(createdAt, ko)}

@@ -72,7 +57,7 @@ export default function ArticleItem({ articleData, onClick }: ArticleItemProps)
{!!images?.length && (
- 이미지 + 이미지
)} diff --git a/packages/web/src/app/[lng]/(main)/community/detail/[articleId]/components/ArticleItem.tsx b/packages/web/src/app/[lng]/(main)/community/detail/[articleId]/components/ArticleItem.tsx index 903103d84..39b5ef194 100644 --- a/packages/web/src/app/[lng]/(main)/community/detail/[articleId]/components/ArticleItem.tsx +++ b/packages/web/src/app/[lng]/(main)/community/detail/[articleId]/components/ArticleItem.tsx @@ -114,7 +114,13 @@ export default function ArticleItem({ article }: ArticleItemProps) { open(() => ) } > - article_image + article_image ))} diff --git a/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatCardList.tsx b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatCardList.tsx new file mode 100644 index 000000000..809b31461 --- /dev/null +++ b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatCardList.tsx @@ -0,0 +1,23 @@ +'use client'; + +import ChatCard from './ChatItem'; +import { chatList } from './dummy'; + +import { useTranslation } from '@/app/i18n/client'; +import { Empty } from '@/components/Empty'; +import { ItemList } from '@/components/List'; + +export default function ChatCardList() { + const { t } = useTranslation('grouping'); + + return ( + <> + } + renderEmpty={() => } + /> +
+ + ); +} diff --git a/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatItem.tsx b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatItem.tsx new file mode 100644 index 000000000..0ca7c0eb7 --- /dev/null +++ b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatItem.tsx @@ -0,0 +1,64 @@ +import { ko } from 'date-fns/locale'; +import Image from 'next/image'; + +import { Chat } from './dummy'; + +import type { HTMLAttributes, PropsWithChildren } from 'react'; + +import ImageSample from '@/components/Image/ImageSample'; +import { Flex } from '@/components/Layout'; +import { NavLink } from '@/components/NavLink'; +import { Spacing } from '@/components/Spacing'; +import cn from '@/utils/cn'; +import { formatDate } from '@/utils/formatDate'; + +interface ChatCardProps extends HTMLAttributes { + chatData: Chat; +} + +export default function ChatCard({ chatData, children }: PropsWithChildren) { + const { title, content, imageUrl, newMessag, groupId, latestMessageTiem } = chatData; + + return ( + + +
+ {imageUrl ? ( + group + ) : ( + + )} +
+ + + +
+ + +

{title}

+

{content}

+
+ +

+ {formatDate(latestMessageTiem, ko)} +

+ {newMessag && ( +
+ {newMessag} +
+ )} +
+
+
+
+ {children} +
+ ); +} diff --git a/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatListHeader.tsx b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatListHeader.tsx index 31bfb05c8..42ff54c42 100644 --- a/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatListHeader.tsx +++ b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/ChatListHeader.tsx @@ -1,15 +1,16 @@ 'use client'; -import { useTranslation } from 'react-i18next'; - import { IconButton } from '@/components/Button'; import { Header } from '@/components/Header'; import { Icon } from '@/components/Icon'; import useAppRouter from '@/hooks/useAppRouter'; -export default function ChatListHeader() { +interface ChatListHeaderProps { + title: string; +} + +export default function ChatListHeader({ title }: ChatListHeaderProps) { const { back } = useAppRouter(); - const { t } = useTranslation('grouping'); return (
@@ -17,7 +18,7 @@ export default function ChatListHeader() { back()}> -

{t('chat.listHeader')}

+

{title}

); diff --git a/packages/web/src/app/[lng]/(main)/grouping/chatList/components/dummy.ts b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/dummy.ts new file mode 100644 index 000000000..47b0fe742 --- /dev/null +++ b/packages/web/src/app/[lng]/(main)/grouping/chatList/components/dummy.ts @@ -0,0 +1,44 @@ +export interface Chat { + title: string; + content: string; + imageUrl: string; + newMessag?: number; + groupId: number; + latestMessageTiem: string; +} + +export const chatList: Chat[] = [ + { + title: '응가 뿌직', + content: '응가좀 치는사람 모이셈', + newMessag: 2, + imageUrl: '/images/approve_character.png', + groupId: 10, + latestMessageTiem: '2024-04-06T20:32', + }, + { + title: '응가 뿌직', + content: '응가좀 치는사람 모이셈', + newMessag: 1, + imageUrl: '/images/approve_character.png', + groupId: 10, + latestMessageTiem: '2024-04-10T18:44', + }, + { + title: '응가 뿌직', + content: '응가좀 치는사람 모이셈', + + imageUrl: '/images/approve_character.png', + groupId: 10, + latestMessageTiem: '2024-04-06T20:32', + }, + { + title: '응가 뿌직', + content: '응가좀 치는사람 모이셈', + newMessag: 3, + + imageUrl: '/images/approve_character.png', + groupId: 10, + latestMessageTiem: '2024-04-06T20:32', + }, +]; diff --git a/packages/web/src/app/[lng]/(main)/grouping/chatList/page.tsx b/packages/web/src/app/[lng]/(main)/grouping/chatList/page.tsx index c459cccdf..97bbee0f2 100644 --- a/packages/web/src/app/[lng]/(main)/grouping/chatList/page.tsx +++ b/packages/web/src/app/[lng]/(main)/grouping/chatList/page.tsx @@ -1,10 +1,21 @@ +import ChatCardList from './components/ChatCardList'; import ChatListHeader from './components/ChatListHeader'; -export default function ChatListPage() { +import { serverTranslation } from '@/app/i18n'; + +interface ChatListPageProps { + params: { + lng: string; + }; +} + +export default async function ChatListPage({ params: { lng } }: ChatListPageProps) { + const { t } = await serverTranslation(lng, 'grouping'); + return ( <> - -
+ + ); } diff --git a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/chat/page.tsx b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/chat/page.tsx deleted file mode 100644 index 1137c5471..000000000 --- a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/chat/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function ChatPage() { - return
; -} diff --git a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetail.tsx b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetail.tsx index e4a42bdf8..aa9d501af 100644 --- a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetail.tsx +++ b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetail.tsx @@ -1,8 +1,10 @@ 'use client'; -import dynamic from 'next/dynamic'; -import { Suspense } from 'react'; +import { useSearchParams } from 'next/navigation'; +import ArticlesContent from './articles/ArticlesContent'; +import ChatContent from './chat/ChatContent'; +import DetailContent from './detail/DetailContent'; import TopSection from './TopSection'; import { useGetGroupDetail } from '@/apis/groups'; @@ -12,34 +14,34 @@ import { Spacing } from '@/components/Spacing'; import { Tabs } from '@/components/Tabs'; import { useNumberParams } from '@/hooks/useNumberParams'; -const DetailContent = dynamic(() => import('./detail/DetailContent')); -const ArticlesContent = dynamic(() => import('./articles/ArticlesContent')); - -export default function GroupDetailPage() { +export default function GroupDetail() { const { t } = useTranslation('groupDetail'); const { groupId } = useNumberParams<['groupId']>(); + + const searchParams = useSearchParams().get('tab'); + const { data: groupDetailData } = useGetGroupDetail(groupId); const { myGroup } = groupDetailData; return ( <> - + {searchParams !== 'chat' && } + {/* */} - - - + - - - + + {/* + + */} diff --git a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetailHeader.tsx b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetailHeader.tsx index 2786ae6bc..068dc0861 100644 --- a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetailHeader.tsx +++ b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/GroupDetailHeader.tsx @@ -1,5 +1,6 @@ 'use client'; +import { useSearchParams } from 'next/navigation'; import { Suspense } from 'react'; import BlockDoneModal from '../../../components/BlockDoneModal'; @@ -20,9 +21,10 @@ import { useBlockStore } from '@/store/useBlockStore'; export default function GroupDetailHeader() { const { back } = useAppRouter(); const { groupId } = useNumberParams<['groupId']>(); + const searchParams = useSearchParams().get('tab'); return ( -
+
back()}> diff --git a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/chat/ChatContent.tsx b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/chat/ChatContent.tsx new file mode 100644 index 000000000..d5d2c389f --- /dev/null +++ b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/components/chat/ChatContent.tsx @@ -0,0 +1,29 @@ +import { usePathname } from 'next/navigation'; + +import MessageForm from '@/components/Form/MessageForm'; +import { Spacing } from '@/components/Spacing'; +import { useNumberParams } from '@/hooks/useNumberParams'; + +export default function ChatContent() { + const pathname = usePathname(); + const { groupId } = useNumberParams<['groupId']>(); + + return ( +
+ + +
+
+
+
+
+
+
+
+
+
+ + +
+ ); +} diff --git a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/page.tsx b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/page.tsx index 819ebbb12..c130d07c9 100644 --- a/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/page.tsx +++ b/packages/web/src/app/[lng]/(main)/grouping/detail/[groupId]/page.tsx @@ -1,6 +1,6 @@ import { ErrorBoundary } from 'react-error-boundary'; -import GroupDetailPage from './components/GroupDetail'; +import GroupDetail from './components/GroupDetail'; import GroupDetailHeader from './components/GroupDetailHeader'; import { Keys, getGroupDetail, getGroupMembers, getNotices } from '@/apis/groups'; @@ -32,7 +32,7 @@ export default async function GroupingDetailPage({ params }: GroupingDetailPageP Keys.getNotices(groupId), ]} > - + diff --git a/packages/web/src/app/[lng]/(main)/profile/components/ProfileDetailSection.tsx b/packages/web/src/app/[lng]/(main)/profile/components/ProfileDetailSection.tsx index 1cda6e9bc..0c35fb7b5 100644 --- a/packages/web/src/app/[lng]/(main)/profile/components/ProfileDetailSection.tsx +++ b/packages/web/src/app/[lng]/(main)/profile/components/ProfileDetailSection.tsx @@ -88,7 +88,7 @@ export default function ProfileDetailSection({ profileData }: ProfileDetailProps

{countryImage && (
- 국가 + 국가
)} {nickname} diff --git a/packages/web/src/app/i18n/locales/ko/groupDetail.json b/packages/web/src/app/i18n/locales/ko/groupDetail.json index 2b84abf08..00b5f21d4 100644 --- a/packages/web/src/app/i18n/locales/ko/groupDetail.json +++ b/packages/web/src/app/i18n/locales/ko/groupDetail.json @@ -78,6 +78,7 @@ "board": { "tab": "게시판", "notice": "공지사항", + "chat": "채팅방", "emptyNotice": "등록된 공지사항이 없어요.", "commentCount": "댓글 {{commentCount}}개" }, diff --git a/packages/web/src/components/Avatar/Avatar.tsx b/packages/web/src/components/Avatar/Avatar.tsx index 9be69fa1d..1e878463e 100644 --- a/packages/web/src/components/Avatar/Avatar.tsx +++ b/packages/web/src/components/Avatar/Avatar.tsx @@ -74,7 +74,7 @@ export default function Avatar({ )}
{countryImage && ( - 국가 + 국가 )}

@@ -84,7 +84,6 @@ export default function Avatar({ } const AvatarImage = memo(function ({ - size, imageUrl, isPending, }: Pick) { @@ -96,19 +95,12 @@ const AvatarImage = memo(function ({ ); } - const imageSize = { - 'x-small': '1.7rem', - small: '2.5rem', - medium: '3.5rem', - large: '6rem', - }; - if (imageUrl) { return ( avatar diff --git a/packages/web/src/components/Card/GroupingCard.tsx b/packages/web/src/components/Card/GroupingCard.tsx index b9a53c15e..c07be6575 100644 --- a/packages/web/src/components/Card/GroupingCard.tsx +++ b/packages/web/src/components/Card/GroupingCard.tsx @@ -63,9 +63,8 @@ export default function GroupingCard({ src={imageUrl} alt="group" className="rounded-8 object-cover" + sizes="64px" loading="lazy" - placeholder="blur" - blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFklEQVR42mN8//HLfwYiAOOoQvoqBABbWyZJf74GZgAAAABJRU5ErkJggg==" /> ) : ( diff --git a/packages/web/src/components/Footer/Footer.tsx b/packages/web/src/components/Footer/Footer.tsx index 442305cc8..6f2d912ca 100644 --- a/packages/web/src/components/Footer/Footer.tsx +++ b/packages/web/src/components/Footer/Footer.tsx @@ -1,4 +1,5 @@ 'use client'; +import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { ButtonAnimation } from '../Animation'; @@ -65,7 +66,7 @@ export default function Footer({ isSpacing = true, spacingColor }: FooterProps) 'text-sign-tertiary': !isSelected(tab), })} > - +

{t(tab.name)}

-
+ ))} diff --git a/packages/web/src/components/Form/MessageForm.tsx b/packages/web/src/components/Form/MessageForm.tsx new file mode 100644 index 000000000..4111fc8dc --- /dev/null +++ b/packages/web/src/components/Form/MessageForm.tsx @@ -0,0 +1,62 @@ +'use client'; + +import { useRef } from 'react'; +import { useForm } from 'react-hook-form'; + +import { useTranslation } from '@/app/i18n/client'; +import { Icon } from '@/components/Icon'; +import { TextFieldController } from '@/components/TextField'; +import cn from '@/utils/cn'; + +export type CreateCommentRequest = { + content: string; +}; + +export default function MessageForm() { + const { t } = useTranslation('community'); + const textareaRef = useRef(null); + const hookForm = useForm({ + mode: 'onChange', + defaultValues: { + content: '', + }, + }); + + const { handleSubmit, reset, watch, register, setFocus } = hookForm; + + return ( +
+
+
+ 0, + })} + rightCaption="" + /> +
+ + +
+
+ ); +} diff --git a/packages/web/src/components/Modal/BottomSheet.tsx b/packages/web/src/components/Modal/BottomSheet.tsx index 3222fbea2..bff415823 100644 --- a/packages/web/src/components/Modal/BottomSheet.tsx +++ b/packages/web/src/components/Modal/BottomSheet.tsx @@ -1,4 +1,5 @@ 'use client'; + import { forwardRef } from 'react'; import Sheet, { type SheetRef } from 'react-modal-sheet'; diff --git a/packages/web/src/components/Modal/ImageModal.tsx b/packages/web/src/components/Modal/ImageModal.tsx index a34331ba5..6e1b93d04 100644 --- a/packages/web/src/components/Modal/ImageModal.tsx +++ b/packages/web/src/components/Modal/ImageModal.tsx @@ -48,7 +48,7 @@ export default function ImageModal({ images, currentImage, onClose }: ImageModal > {images.map((image, index) => (
- {`img-${index}`} + {`img-${index}`}
))} diff --git a/packages/web/src/utils/formatDate.ts b/packages/web/src/utils/formatDate.ts new file mode 100644 index 000000000..05cb85b30 --- /dev/null +++ b/packages/web/src/utils/formatDate.ts @@ -0,0 +1,14 @@ +import { format, formatDistanceToNow } from 'date-fns'; + +export const formatDate = (date: string, locale: Locale) => { + const d = new Date(date); + const now = Date.now(); + const diff = (now - d.getTime()) / 1000; + if (diff < 60 * 1) { + return '방금 전'; + } + if (diff < 60 * 60 * 24 * 3) { + return formatDistanceToNow(d, { addSuffix: true, locale }); + } + return format(d, 'MM/dd', { locale }); +}; diff --git a/packages/web/src/utils/formatMeetingDate.ts b/packages/web/src/utils/formatMeetingDate.ts index 91e61acbb..33bd49716 100644 --- a/packages/web/src/utils/formatMeetingDate.ts +++ b/packages/web/src/utils/formatMeetingDate.ts @@ -1,6 +1,7 @@ -import { DAY_OF_WEEK } from '@/constants'; import { format, getDay, parseISO } from 'date-fns'; +import { DAY_OF_WEEK } from '@/constants'; + export function formatMeetingDate(meetDate: string, startTime: string) { const startDate = parseISO(meetDate); const dayOfWeekIndex = getDay(startDate);