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
3 changes: 3 additions & 0 deletions components/Headers/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import { Profile } from 'types/profile';

import Menu from '../Menu';
import { useSnackbar } from 'context/SnackBarContext';

interface LoginProps {
isMobile: boolean;
Expand All @@ -23,6 +24,7 @@ interface LoginProps {
export default function Login({ isMobile, isLoggedIn, profile }: LoginProps) {
const [isOpen, setIsOpen] = useState(false);
const [profileMenu, setProfileMenu] = useState<string[]>([]);
const { showSnackbar } = useSnackbar();

const profileImage = profile?.image ?? '/icon/icon-profile.svg';
const router = useRouter();
Expand Down Expand Up @@ -56,6 +58,7 @@ export default function Login({ isMobile, isLoggedIn, profile }: LoginProps) {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
await router.push('/');
showSnackbar('로그아웃 되었습니다.', 'fail');
}
};

Expand Down
17 changes: 4 additions & 13 deletions components/Modal/ImageUploadModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import React, { useEffect, useRef, useState } from 'react';

import Button from '../Button';
import Modal from './Modal';
import SnackBar from '../SnackBar';
import useSnackBar from '@/hooks/useSanckBar';
import { useSnackbar } from 'context/SnackBarContext';

interface ImageUploadModalProps {
imageFile?: File | null;
Expand All @@ -29,20 +28,20 @@ const ImageUploadModal = ({
const [imageFile, setImageFile] = useState<File | null>(initialImageFile);
const [isDragging, setIsDragging] = useState(false);
const [isValidFile, setIsValidFile] = useState<boolean>(true);
const { snackBarValues, snackBarOpen } = useSnackBar();
const { showSnackbar } = useSnackbar();

const validateFile = (file: File): boolean => {
setIsValidFile(true);

const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
snackBarOpen('fail', NOT_SVG_MESSAGE);
showSnackbar(NOT_SVG_MESSAGE, 'fail');
setIsValidFile(false);
return false;
}

if (file.size > MAX_FILE_SIZE) {
snackBarOpen('fail', SIZE_LIMIT_MESSAGE);
showSnackbar(SIZE_LIMIT_MESSAGE, 'fail');
setIsValidFile(false);
return false;
}
Expand Down Expand Up @@ -210,14 +209,6 @@ const ImageUploadModal = ({
</Button>
</div>
</div>

<SnackBar
severity={snackBarValues.severity}
onClose={snackBarValues.onClose}
open={snackBarValues.open}
>
{snackBarValues.children}
</SnackBar>
</Modal>
);
};
Expand Down
21 changes: 12 additions & 9 deletions components/SnackBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ export interface SnackBarProps {
const severityConfig = {
fail: {
style:
'bg-red-50 border-red-100 fixed left-1/2 transform -translate-x-1/2 top-[120px] mx-auto mo:bottom-[80px] mo:top-auto whitespace-nowrap shadow-custom',
'bg-red-50 border-red-100 fixed left-1/2 transform -translate-x-1/2 top-[120px] mx-auto mo:bottom-[80px] mo:top-auto shadow-custom mo:min-w-[280px]',
icon: '/icon/icon-fail.svg',
textStyle: 'text-red-100 text-14sb mo:text-12sb',
textStyle: 'text-red-100 text-14sb mo:text-12sb ',
},
success: {
style:
'bg-green-100 border-green-200 fixed left-1/2 transform -translate-x-1/2 top-[120px] mx-auto mo:bottom-[80px] mo:top-auto whitespace-nowrap shadow-custom',
'bg-green-100 border-green-200 fixed left-1/2 transform -translate-x-1/2 top-[120px] mx-auto mo:bottom-[80px] mo:top-auto shadow-custom mo:min-w-[280px]',
icon: '/icon/icon-success.svg',
textStyle: 'text-green-300 text-14sb mo:text-12sb',
textStyle: 'text-green-300 text-14sb mo:text-12sb ',
},
info: {
style: 'bg-background border-background',
Expand All @@ -36,14 +36,14 @@ const severityConfig = {
* @param children - Snackbar에 표시할 메시지
* @param open - Snackbar가 열려있는지 여부
* @param onClose - Snackbar를 닫기 위한 함수
* @param autoHideDuration - 자동으로 닫히는 시간 (밀리초) 기본값은 3000
* @param autoHideDuration - 자동으로 닫히는 시간 (밀리초) 기본값은 2000
*/
export default function SnackBar({
severity,
children,
open,
onClose,
autoHideDuration = 3000,
autoHideDuration = 2000,
}: SnackBarProps) {
const { style, icon, textStyle } =
severityConfig[severity ? severity : 'info'];
Expand All @@ -55,7 +55,7 @@ export default function SnackBar({
if (autoHideDuration) {
const timer = setTimeout(() => {
setVisible(false);
setTimeout(onClose, 300);
setTimeout(onClose, 200);
}, autoHideDuration);
return () => clearTimeout(timer);
}
Expand All @@ -66,13 +66,16 @@ export default function SnackBar({

return (
<div
role="alert"
aria-live="assertive"
aria-atomic="true"
className={`rounded-custom ${style} flex items-center gap-[15px] border px-5 py-[11px] transition-opacity duration-300 mo:px-[15px] mo:py-[11px] ${
visible ? 'opacity-100' : 'opacity-0'
}`}
style={{ display: open || visible ? 'flex' : 'none' }}
style={{ display: open || visible ? 'flex' : 'none', zIndex: 9999 }}
>
{icon && <Image src={icon} alt="snackbar icon" width={20} height={20} />}
<p className={`${textStyle} break-words mo:break-words`}>{children}</p>
<p className={`${textStyle} whitespace-pre-line`}>{children}</p>
</div>
);
}
41 changes: 10 additions & 31 deletions components/boards.page/BoardDetailCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { BoardBase, Writer } from 'types/board';
import Button from '@/components/Button';
import EditorViewer from '@/components/EditorViewer';
import Heart from '@/components/Heart/Heart';
import SnackBar, { SnackBarProps } from '@/components/SnackBar';
import useCheckMobile from '@/hooks/useCheckMobile';
import instance from '@/lib/axios-client';
import dateConversion from '@/utils/dateConversion';

import ButtonIcon from './ButtonIcon';
import { useProfileContext } from '@/hooks/useProfileContext';
import ModalDefault from '../Modal/ModalDefault';
import { useSnackbar } from 'context/SnackBarContext';

interface BoardDetailCard extends BoardBase {
title: string;
Expand Down Expand Up @@ -46,17 +46,14 @@ export default function BoardDetailCard({
}: BoardDetailCard & Writer) {
const [isLiked, setIsLiked] = useState(initialIsLiked);
const [likeCountState, setLikeCountState] = useState(likeCount);
const [snackBarOpen, setSnackBarOpen] = useState(false);
const [snackBarMessage, setSnackBarMessage] = useState('');
const [snackStyled, setSnackStyled] =
useState<SnackBarProps['severity']>(undefined);
const [isModal, setIsModal] = useState(false);
const [isUpdate, setIsUpdate] = useState(false);
const isMobile = useCheckMobile();
const router = useRouter();
const { articleId } = router.query;
const id = articleId as string;
const { isAuthenticated } = useProfileContext();
const { showSnackbar } = useSnackbar();

useEffect(() => {
if (createdAt !== updatedAt) {
Expand All @@ -81,16 +78,10 @@ export default function BoardDetailCard({
try {
await instance.delete(`/articles/${id}`);
setIsModal(false);
setSnackBarMessage(
'게시글이 삭제되었습니다. 게시판 메인으로 이동합니다.'
);
setSnackStyled('success');
setSnackBarOpen(true);
setTimeout(() => {
router.push('/boards');
}, 2500);
router.push('/boards');
showSnackbar('게시글이 삭제되었습니다.', 'success');
} catch (error) {
console.error('게시글을 삭제하지 못했습니다.', error);
showSnackbar('게시글을 삭제하지 못했습니다.', 'fail');
}
};

Expand All @@ -104,18 +95,15 @@ export default function BoardDetailCard({
setLikeCountState((prevCount) =>
isLiked ? prevCount - 1 : prevCount + 1
);
setSnackBarMessage(
isLiked ? '좋아요가 취소되었습니다.' : '좋아요가 반영되었습니다.'
showSnackbar(
isLiked ? '좋아요가 취소되었습니다.' : '좋아요가 반영되었습니다.',
'success'
);
setSnackBarOpen(true);
setSnackStyled('success');
} catch (error) {
console.error('--- handleHeartClick:error:', error);
showSnackbar('좋아요를 반영하지 못했습니다.', 'fail');
}
} else {
setSnackBarMessage('로그인 후 이용해주세요.');
setSnackBarOpen(true);
setSnackStyled('fail');
showSnackbar('로그인 후 이용해주세요.', 'fail');
}
};

Expand Down Expand Up @@ -172,15 +160,6 @@ export default function BoardDetailCard({
<EditorViewer content={content} />
</div>

<SnackBar
severity={snackStyled}
open={snackBarOpen}
onClose={() => setSnackBarOpen(false)}
autoHideDuration={2000}
>
{snackBarMessage}
</SnackBar>

<ModalDefault
title="알림"
text="게시글을 삭제하시겠습니까?"
Expand Down
36 changes: 3 additions & 33 deletions components/wiki.page/ContentHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Button from '@/components/Button';
import LinkBar from '@/components/LinkBar';
import UnsavedChangesModal from '@/components/Modal/UnsavedChangesModal';
import SnackBar from '@/components/SnackBar';
import { useSnackbar } from 'context/SnackBarContext';

interface ContentHeaderProps {
name: string;
Expand Down Expand Up @@ -41,17 +42,7 @@ export default function ContentHeader({
saveContent,
}: ContentHeaderProps) {
const [isUCOpen, setIsUCOpen] = useState(false);
const [linkSnackBarState, setLinkSnackBarState] = useState<{
open: boolean;
severity: 'fail' | 'success' | 'info';
message: string;
autoHideDuration?: number;
}>({
open: false,
severity: 'success',
message: '',
});

const { showSnackbar } = useSnackbar();
const [infoSnackBarState, setInfoSnackBarState] = useState<{
open: boolean;
severity: 'fail' | 'success' | 'info';
Expand Down Expand Up @@ -88,25 +79,13 @@ export default function ContentHeader({
navigator.clipboard
.writeText(link)
.then(() => {
setLinkSnackBarState({
open: true,
severity: 'success',
message: '클립보드에 복사되었습니다.',
autoHideDuration: 1500,
});
showSnackbar('클립보드에 복사되었습니다.', 'success');
})
.catch(() => {
alert('링크 복사에 실패했습니다.');
});
};

const handleCloseLinkSnackBar = () => {
setLinkSnackBarState({
...linkSnackBarState,
open: false,
});
};

const handleCloseInfoSnackBar = () => {
setInfoSnackBarState({
...infoSnackBarState,
Expand Down Expand Up @@ -151,15 +130,6 @@ export default function ContentHeader({
{infoSnackBarState.message}
</SnackBar>
)}

<SnackBar
severity={linkSnackBarState.severity}
open={linkSnackBarState.open}
onClose={handleCloseLinkSnackBar}
autoHideDuration={linkSnackBarState.autoHideDuration}
>
{linkSnackBarState.message}
</SnackBar>
</div>
);
}
41 changes: 4 additions & 37 deletions components/wiki.page/Contents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import instance from '@/lib/axios-client';
import Blank from './Blank';
import ContentHeader from './ContentHeader';
import { useProfileContext } from '@/hooks/useProfileContext';
import SnackBar from '../SnackBar';
import { useSnackbar } from 'context/SnackBarContext';

interface ProfileProps {
profile: ProfileAnswer;
Expand All @@ -26,17 +26,7 @@ export default function Contents({ profile }: ProfileProps) {
const [isDMOpen, setIsDMOpen] = useState(false);
const [newContent, setNewContent] = useState<string>(profile.content || '');
const [profileData, setProfileData] = useState<ProfileAnswer>(profile);
const [snackBarState, setSnackBarState] = useState<{
open: boolean;
severity: 'fail' | 'success' | 'info';
message: string;
autoHideDuration?: number;
}>({
open: false,
severity: 'fail',
message: '',
autoHideDuration: 1000,
});
const { showSnackbar } = useSnackbar();
const [diffTime, setDiffTime] = useState<number>(0);

const previousContent = useRef<string>(newContent);
Expand All @@ -45,11 +35,7 @@ export default function Contents({ profile }: ProfileProps) {

const handleQuizOpen = async () => {
if (!isAuthenticated) {
setSnackBarState({
open: true,
severity: 'fail',
message: '로그인 후 이용해주세요.',
});
showSnackbar('로그인 후 이용해주세요.', 'fail');
return;
}
const res = await instance.get(`/profiles/${profile.code}/ping`);
Expand All @@ -67,15 +53,7 @@ export default function Contents({ profile }: ProfileProps) {

//퀴즈 성공 후 위키 편집모드
const handleQuizSuccess = async () => {
setSnackBarState({
open: true,
severity: 'success',
message: '정답입니다!',
});

setTimeout(() => {
setSnackBarState((prev) => ({ ...prev, open: false }));
}, 1500);
showSnackbar('정답입니다!', 'success');
setIsQuizOpen(false);

const accessToken = localStorage.getItem('accessToken');
Expand Down Expand Up @@ -182,17 +160,6 @@ export default function Contents({ profile }: ProfileProps) {
diffTime={diffTime}
/>
</div>

<div className="fixed z-20">
<SnackBar
severity={snackBarState.severity}
open={snackBarState.open}
onClose={() => setSnackBarState({ ...snackBarState, open: false })}
autoHideDuration={snackBarState.autoHideDuration}
>
{snackBarState.message}
</SnackBar>
</div>
</div>
<WikiQuizModal
isOpen={isQuizOpen}
Expand Down
Loading
Loading