toast
This commit is contained in:
@@ -8,7 +8,7 @@ type CommonErrorProps = FallbackProps & {
|
||||
height?: number;
|
||||
};
|
||||
export const APIError = ({ error, resetErrorBoundary }: CommonErrorProps) => {
|
||||
const { navigateBack } = useNavigate();
|
||||
const { reload } = useNavigate();
|
||||
const msg = useMemo(() => {
|
||||
let message: Partial<DialogProps> = {
|
||||
title: '일시적인 오류가 발생하였습니다.',
|
||||
@@ -21,7 +21,7 @@ export const APIError = ({ error, resetErrorBoundary }: CommonErrorProps) => {
|
||||
}, [error]);
|
||||
|
||||
const handleCancel = () => {
|
||||
navigateBack();
|
||||
reload();
|
||||
resetErrorBoundary();
|
||||
};
|
||||
|
||||
@@ -30,8 +30,8 @@ export const APIError = ({ error, resetErrorBoundary }: CommonErrorProps) => {
|
||||
afterLeave={() => null}
|
||||
open={true}
|
||||
onClose={() => null}
|
||||
onConfirmClick={resetErrorBoundary}
|
||||
onCancelClick={handleCancel}
|
||||
onConfirmClick={ resetErrorBoundary }
|
||||
onCancelClick={ handleCancel }
|
||||
message={msg.message}
|
||||
buttonLabel={['취소', '재시도']}
|
||||
/>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Fragment } from 'react/jsx-runtime';
|
||||
import { useAppColor } from '@/shared/lib/hooks/use-change-bg-color';
|
||||
|
||||
export const IOSStatusBar = () => {
|
||||
const [appColor] = useAppColor();
|
||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
|
||||
if(!isIOS){
|
||||
return <Fragment></Fragment>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ios-status-bar" style={{ backgroundColor: appColor }}></div>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
interface EventListeners {
|
||||
element: HTMLElement | null;
|
||||
onScroll: () => void;
|
||||
onTouchStart: () => void;
|
||||
onTouchMove: () => void;
|
||||
onTouchEnd: () => void;
|
||||
}
|
||||
|
||||
export const addEventListeners = ({ element, onScroll, onTouchStart, onTouchMove, onTouchEnd }: EventListeners) => {
|
||||
element?.addEventListener('scroll', onScroll);
|
||||
element?.addEventListener('touchstart', onTouchStart, { passive: true });
|
||||
element?.addEventListener('touchmove', onTouchMove, { passive: true });
|
||||
element?.addEventListener('touchend', onTouchEnd, { passive: true });
|
||||
};
|
||||
|
||||
export const removeEventListeners = ({ element, onScroll, onTouchStart, onTouchMove, onTouchEnd }: EventListeners) => {
|
||||
element?.removeEventListener('scroll', onScroll);
|
||||
element?.removeEventListener('touchstart', onTouchStart);
|
||||
element?.removeEventListener('touchmove', onTouchMove);
|
||||
element?.removeEventListener('touchend', onTouchEnd);
|
||||
};
|
||||
@@ -1,103 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { AnimatePresence, motion, useMotionValue } from 'framer-motion';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { useRouteChangeEffect } from './use-route-change-effect';
|
||||
import { useScrollEventListeners } from './use-scroll-event-listeners';
|
||||
import { useScrollTarget } from './use-scroll-target';
|
||||
|
||||
const TIME_OUT = 5000;
|
||||
const THRASH_HOLD = 30;
|
||||
|
||||
let isScrollingBackToTop = false;
|
||||
let lastScrollY = 0;
|
||||
let isTouching = false;
|
||||
|
||||
export const TopButton = () => {
|
||||
const scrollTarget = useScrollTarget();
|
||||
const scrollY = useMotionValue(scrollTarget.current?.scrollTop ?? 0);
|
||||
|
||||
const scrollTimeOutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
const [showButton, setShowButton] = useState<boolean | undefined>();
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setShowButton(false);
|
||||
isScrollingBackToTop = false;
|
||||
isTouching = false;
|
||||
if (scrollTarget.current) {
|
||||
scrollTarget.current.scrollTop = 0;
|
||||
}
|
||||
clearTimeout(scrollTimeOutRef.current);
|
||||
}, []);
|
||||
|
||||
const initialize = useCallback(() => {
|
||||
reset();
|
||||
lastScrollY = 0;
|
||||
}, []);
|
||||
|
||||
const scrollBackToTop = useCallback(() => {
|
||||
reset();
|
||||
}, []);
|
||||
|
||||
const onScroll = () => {
|
||||
if (isTouching) return;
|
||||
const currentScrollY = scrollTarget.current?.scrollTop ?? 0;
|
||||
const direction = currentScrollY > lastScrollY ? 'down' : 'up';
|
||||
lastScrollY = currentScrollY <= 0 ? 0 : currentScrollY;
|
||||
|
||||
scrollY.set(currentScrollY);
|
||||
clearTimeout(scrollTimeOutRef.current);
|
||||
|
||||
setShowButton(direction === 'down' ? false : currentScrollY > THRASH_HOLD);
|
||||
|
||||
if (!isScrollingBackToTop && direction === 'down') {
|
||||
scrollTimeOutRef.current = setTimeout(() => {
|
||||
setShowButton(true);
|
||||
}, TIME_OUT);
|
||||
}
|
||||
if (currentScrollY === 0) {
|
||||
isScrollingBackToTop = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onTouchStart = useCallback(() => {
|
||||
isTouching = true;
|
||||
}, []);
|
||||
const onTouchMove = useCallback(() => {
|
||||
isTouching = true;
|
||||
}, []);
|
||||
const onTouchEnd = useCallback(() => {
|
||||
isTouching = false;
|
||||
}, []);
|
||||
|
||||
useScrollEventListeners({
|
||||
scrollTarget,
|
||||
onScroll,
|
||||
onTouchStart,
|
||||
onTouchMove,
|
||||
onTouchEnd,
|
||||
});
|
||||
|
||||
useRouteChangeEffect({
|
||||
scrollTarget,
|
||||
onScroll,
|
||||
onTouchStart,
|
||||
onTouchMove,
|
||||
onTouchEnd,
|
||||
callback: initialize,
|
||||
});
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{showButton && (
|
||||
<motion.button
|
||||
onClick={scrollBackToTop}
|
||||
className={'btn goto-top-btn'}
|
||||
initial={{ translateY: '110%' }}
|
||||
animate={{ translateY: '0%' }}
|
||||
>
|
||||
위로
|
||||
</motion.button>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useIsFetching, useIsMutating } from '@tanstack/react-query';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
import { useRouterListener } from '@/shared/lib/hooks';
|
||||
import { addEventListeners, removeEventListeners } from './handle-event-listeners';
|
||||
|
||||
interface RouteChangeEffect {
|
||||
scrollTarget: React.MutableRefObject<HTMLElement | null>;
|
||||
onScroll: () => void;
|
||||
onTouchStart: () => void;
|
||||
onTouchMove: () => void;
|
||||
onTouchEnd: () => void;
|
||||
callback: () => void;
|
||||
}
|
||||
|
||||
export const useRouteChangeEffect = ({
|
||||
scrollTarget,
|
||||
onScroll,
|
||||
onTouchStart,
|
||||
onTouchMove,
|
||||
onTouchEnd,
|
||||
callback,
|
||||
}: RouteChangeEffect) => {
|
||||
const isFetching = useIsFetching();
|
||||
const isMutating = useIsMutating();
|
||||
const isLoading = isFetching > 0 || isMutating > 0;
|
||||
|
||||
const resetScrollTarget = useCallback(() => {
|
||||
const rootEl = document.getElementById('root') as HTMLElement;
|
||||
const pullToRefreshEl = document.getElementsByClassName('ptr__children')[0] as HTMLElement;
|
||||
|
||||
scrollTarget.current = rootEl;
|
||||
if (pullToRefreshEl?.scrollHeight > window.innerHeight) {
|
||||
scrollTarget.current = pullToRefreshEl;
|
||||
addEventListeners({ element: pullToRefreshEl, onScroll, onTouchStart, onTouchMove, onTouchEnd });
|
||||
removeEventListeners({ element: rootEl, onScroll, onTouchStart, onTouchMove, onTouchEnd });
|
||||
} else {
|
||||
addEventListeners({ element: rootEl, onScroll, onTouchStart, onTouchMove, onTouchEnd });
|
||||
removeEventListeners({ element: pullToRefreshEl, onScroll, onTouchStart, onTouchMove, onTouchEnd });
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (isLoading) return;
|
||||
resetScrollTarget();
|
||||
}, [isLoading]);
|
||||
|
||||
useRouterListener(() => {
|
||||
setTimeout(() => {
|
||||
callback?.();
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { MutableRefObject, useEffect } from 'react';
|
||||
|
||||
import { addEventListeners, removeEventListeners } from './handle-event-listeners';
|
||||
|
||||
interface EventListeners {
|
||||
scrollTarget: MutableRefObject<HTMLElement | null>;
|
||||
onScroll: () => void;
|
||||
onTouchStart: () => void;
|
||||
onTouchMove: () => void;
|
||||
onTouchEnd: () => void;
|
||||
}
|
||||
export const useScrollEventListeners = ({
|
||||
scrollTarget,
|
||||
onScroll,
|
||||
onTouchStart,
|
||||
onTouchMove,
|
||||
onTouchEnd,
|
||||
}: EventListeners) => {
|
||||
useEffect(() => {
|
||||
if(!scrollTarget.current){
|
||||
return () => {};
|
||||
}
|
||||
addEventListeners({ element: scrollTarget.current, onScroll, onTouchStart, onTouchMove, onTouchEnd });
|
||||
|
||||
return () => {
|
||||
const rootEl = document.getElementById('root') as HTMLElement;
|
||||
const pullToRefreshEl = document.getElementsByClassName('ptr__children')[0] as HTMLElement;
|
||||
removeEventListeners({ element: rootEl, onScroll, onTouchStart, onTouchMove, onTouchEnd });
|
||||
removeEventListeners({ element: pullToRefreshEl, onScroll, onTouchStart, onTouchMove, onTouchEnd });
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export const useScrollTarget = () => {
|
||||
const scrollTarget = useRef<HTMLElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const rootEl = document.getElementById('root') as HTMLElement;
|
||||
const pullToRefreshEl = document.getElementsByClassName('ptr__children')[0] as HTMLElement;
|
||||
|
||||
let target: HTMLElement | null = rootEl;
|
||||
if (pullToRefreshEl?.scrollHeight > window.innerHeight) {
|
||||
target = pullToRefreshEl;
|
||||
}
|
||||
scrollTarget.current = target;
|
||||
|
||||
return () => {
|
||||
scrollTarget.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return scrollTarget;
|
||||
};
|
||||
Reference in New Issue
Block a user