홈 즐겨찾기 변경
This commit is contained in:
@@ -84,7 +84,6 @@
|
||||
"react-tooltip": "^5.29.1",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-use": "^17.6.0",
|
||||
"recoil": "^0.7.7",
|
||||
"rimraf": "^6.0.1",
|
||||
"root": "git+https://github.com/tanstack/react-query.git",
|
||||
"source-map-loader": "^5.0.0",
|
||||
|
||||
537
pnpm-lock.yaml
generated
537
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
import { Splash } from '@/widgets/splash/splash';
|
||||
import { Suspense } from 'react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { CookiesProvider } from 'react-cookie';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { GlobalErrorBoundary } from '@/widgets/error-boundaries';
|
||||
@@ -16,14 +15,12 @@ export const AppProvider = ({
|
||||
}: AppProviderProps) => {
|
||||
return (
|
||||
<GlobalErrorBoundary>
|
||||
<RecoilRoot>
|
||||
<QueryClientProvider client={ getGlobalQueryClient() }>
|
||||
<Suspense fallback={<Splash />}>
|
||||
<CookiesProvider>{children}</CookiesProvider>
|
||||
{/* <ReactQueryDevtools /> */}
|
||||
</Suspense>
|
||||
</QueryClientProvider>
|
||||
</RecoilRoot>
|
||||
</GlobalErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -72,19 +72,14 @@ export interface HeaderNavigationProps {
|
||||
headerTitle?: string;
|
||||
headerLeft?: React.ReactNode;
|
||||
headerRight?: React.ReactNode;
|
||||
menuOn: boolean;
|
||||
headerType: HeaderType;
|
||||
setMenuOn: (menuOn: boolean) => void;
|
||||
favoriteEdit?: boolean;
|
||||
loginSuccess: boolean;
|
||||
mid?: string;
|
||||
setMid: (mid: string) => void;
|
||||
};
|
||||
|
||||
export interface FooterProps {
|
||||
setMenuOn: (menuOn: boolean) => void;
|
||||
footerCurrentPage?: string | null;
|
||||
setFavoriteEdit: (favoriteEdit: boolean) => void;
|
||||
};
|
||||
export enum FooterItemActiveKey {
|
||||
Home = 'Home',
|
||||
|
||||
@@ -4,29 +4,26 @@ import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import 'swiper/css';
|
||||
import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { UserFavorite } from '@/entities/user/model/types';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import { useFavoriteEditOnStore, useMenuOnStore, useStore } from '@/shared/model/store';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { showAlert } from '@/widgets/show-alert';
|
||||
import { checkGrant } from '@/shared/lib/check-grant';
|
||||
|
||||
export interface FavoriteWrapperProps {
|
||||
usingType: 'home' | 'menu',
|
||||
editMode?: boolean,
|
||||
setEditMode?: (editMode: boolean) => void;
|
||||
changeMenuId?: string;
|
||||
setMenuOn?: (menuOn: boolean) => void;
|
||||
};
|
||||
|
||||
export const FavoriteWrapper = ({
|
||||
usingType,
|
||||
editMode,
|
||||
setEditMode,
|
||||
changeMenuId,
|
||||
setMenuOn
|
||||
}: FavoriteWrapperProps) => {
|
||||
const { navigate } = useNavigate();
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { menuOn, setMenuOn } = useMenuOnStore();
|
||||
const { favoriteEditOn, setFavoriteEditOn } = useFavoriteEditOnStore();
|
||||
|
||||
const [favoriteItems, setFavoriteItems] = useState<Array<UserFavorite>>([]);
|
||||
|
||||
const itemAdd: UserFavorite = {
|
||||
@@ -38,20 +35,17 @@ export const FavoriteWrapper = ({
|
||||
};
|
||||
|
||||
const onClickToFavoriteEdit = () => {
|
||||
if(setEditMode){
|
||||
setEditMode(true);
|
||||
}
|
||||
setMenuOn(true);
|
||||
setFavoriteEditOn(true);
|
||||
};
|
||||
|
||||
const onClickToNavigate = (menuId?: number, path?: string) => {
|
||||
if(menuId && checkGrant(menuId, 'R')){
|
||||
if(!!path){
|
||||
navigate(path);
|
||||
if(setMenuOn){
|
||||
setMenuOn(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
showAlert(t('common.nopermission'));
|
||||
}
|
||||
@@ -73,7 +67,7 @@ export const FavoriteWrapper = ({
|
||||
<SwiperSlide key={ `favorite-slide-key-${i}` }>
|
||||
<div
|
||||
className="swiper-item"
|
||||
onClick={ () => !editMode && onClickToNavigate(favoriteItems[i]?.menuId, favoriteItems[i]?.programPath) }
|
||||
onClick={ () => !favoriteEditOn && onClickToNavigate(favoriteItems[i]?.menuId, favoriteItems[i]?.programPath) }
|
||||
>
|
||||
<div className="swiper-icon coin-icon">
|
||||
<img
|
||||
@@ -90,7 +84,7 @@ export const FavoriteWrapper = ({
|
||||
};
|
||||
const makeAddFavoriteItem = () => {
|
||||
let rs = [];
|
||||
if(!editMode && usingType === 'menu'){
|
||||
if(!favoriteEditOn){
|
||||
rs.push(
|
||||
<SwiperSlide key={ `favorite-item-add-slide-key` }>
|
||||
<div
|
||||
@@ -114,7 +108,7 @@ export const FavoriteWrapper = ({
|
||||
|
||||
useEffect(() => {
|
||||
getFavoriteList();
|
||||
}, [changeMenuId]);
|
||||
}, [changeMenuId, favoriteEditOn]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import { useFavoriteEditOnStore, useMenuOnStore, useStore } from '@/shared/model/store';
|
||||
import { UserFavorite } from '@/entities/user/model/types';
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import { MenuItem } from '../model/types';
|
||||
@@ -13,8 +13,6 @@ export interface MenuCategoryProps {
|
||||
menuName?: string;
|
||||
iconFilePath?: string;
|
||||
subMenu?: Array<MenuItem>;
|
||||
setMenuOn?: (menuOn: boolean) => void;
|
||||
editMode?: boolean;
|
||||
changeMenuId?: string;
|
||||
setChangeMenuId: (menuIdChecked?: string) => void;
|
||||
buttonRefs: RefObject<Array<HTMLDivElement>>;
|
||||
@@ -26,8 +24,6 @@ export const MenuCategory = ({
|
||||
iconFilePath,
|
||||
menuName,
|
||||
subMenu,
|
||||
setMenuOn,
|
||||
editMode,
|
||||
changeMenuId,
|
||||
setChangeMenuId,
|
||||
buttonRefs,
|
||||
@@ -39,9 +35,12 @@ export const MenuCategory = ({
|
||||
const [favoriteItems, setFavoriteItems] = useState<Array<UserFavorite>>([]);
|
||||
const [menuIds, setMenuIds] = useState<Array<number | undefined>>([]);
|
||||
|
||||
const { menuOn, setMenuOn } = useMenuOnStore();
|
||||
const { favoriteEditOn, setFavoriteEditOn } = useFavoriteEditOnStore();
|
||||
|
||||
const onClickToNavigate = (menuId?: number, path?: string) => {
|
||||
if(menuId && checkGrant(menuId, 'R')){
|
||||
if(!!path && !!setMenuOn && !editMode){
|
||||
if(!!path && !favoriteEditOn){
|
||||
setMenuOn(false);
|
||||
navigate(path);
|
||||
}
|
||||
@@ -125,7 +124,7 @@ export const MenuCategory = ({
|
||||
? subMenu[i]?.menuNameEng
|
||||
: subMenu[i]?.menuName;
|
||||
|
||||
if(!!editMode && subMenu[i] && subMenu[i]?.menuId){
|
||||
if(!!favoriteEditOn && subMenu[i] && subMenu[i]?.menuId){
|
||||
rs.push(
|
||||
<li
|
||||
key={ `menu-item-key-${menuId}-${i}` }
|
||||
|
||||
@@ -13,10 +13,9 @@ import {
|
||||
useSetHeaderTitle,
|
||||
useSetHeaderType,
|
||||
useSetFooterMode,
|
||||
useSetFooterCurrentPage
|
||||
useSetFooterCurrentPage,
|
||||
} from '@/widgets/sub-layout/use-sub-layout';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import { UserFavorite } from '@/entities/user/model/types';
|
||||
import { useHomeBannerListMutation } from '@/entities/home/api/use-home-banner-list-mutation';
|
||||
import {
|
||||
BannerList,
|
||||
@@ -27,11 +26,6 @@ import {
|
||||
import { showAlert } from '@/widgets/show-alert';
|
||||
import { snackBar } from '@/shared/lib';
|
||||
|
||||
export let homeReloadKey = 1;
|
||||
export const setHomeReloadKey = () => {
|
||||
homeReloadKey++;
|
||||
};
|
||||
|
||||
export const HomePage = () => {
|
||||
const { openBiometricRegistrationPopup, checkAlarmLink } = useAppBridge();
|
||||
const { mutateAsync: homeBannerList } = useHomeBannerListMutation();
|
||||
@@ -46,7 +40,6 @@ export const HomePage = () => {
|
||||
|
||||
const [bottomBannerOn, setBottomBannerOn] = useState<boolean>(false);
|
||||
const [authRegisterOn, setAuthRegisterOn] = useState<boolean>(false);
|
||||
const [favoriteItems, setFavoriteItems] = useState<Array<UserFavorite>>([]);
|
||||
const [bannerList, setBannerList] = useState<Array<BannerList>>([]);
|
||||
|
||||
const callHomeBannerList = () => {
|
||||
@@ -117,9 +110,6 @@ export const HomePage = () => {
|
||||
else{
|
||||
useStore.getState().UserStore.setFirstAccess(false);
|
||||
}
|
||||
|
||||
let userFavorite = useStore.getState().UserStore.userFavorite;
|
||||
setFavoriteItems(userFavorite);
|
||||
callHomeBannerList();
|
||||
}, []);
|
||||
|
||||
@@ -143,7 +133,6 @@ export const HomePage = () => {
|
||||
<div className="tab-pane dashboard active">
|
||||
<FavoriteWrapper
|
||||
usingType='home'
|
||||
key={ homeReloadKey }
|
||||
></FavoriteWrapper>
|
||||
<DayStatusBox></DayStatusBox>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,31 @@ import { createUserInfoStore, UserInfoState } from '@/entities/user/model/store'
|
||||
import { createBannerInfoStore, BannerInfoState, createCommonStore, CommonState } from '@/entities/common/model/store';
|
||||
import { StorageKeys } from '@/shared/constants/local-storage';
|
||||
|
||||
export interface MenuOnStore {
|
||||
menuOn: boolean;
|
||||
setMenuOn: (menuOn: boolean) => void;
|
||||
};
|
||||
export interface FavoriteEditOnStore {
|
||||
favoriteEditOn: boolean;
|
||||
setFavoriteEditOn: (favoriteEditOn: boolean) => void;
|
||||
};
|
||||
export const useMenuOnStore = create<MenuOnStore>((set) => ({
|
||||
menuOn: false,
|
||||
setMenuOn: (menuOn: boolean) => {
|
||||
set((state: { menuOn: boolean}) => ({
|
||||
menuOn: (state.menuOn = menuOn)
|
||||
}))
|
||||
}
|
||||
}));
|
||||
export const useFavoriteEditOnStore = create<FavoriteEditOnStore>((set) => ({
|
||||
favoriteEditOn: false,
|
||||
setFavoriteEditOn: (favoriteEditOn: boolean) => {
|
||||
set((state: { favoriteEditOn: boolean}) => ({
|
||||
favoriteEditOn: (state.favoriteEditOn = favoriteEditOn)
|
||||
}))
|
||||
}
|
||||
}));
|
||||
|
||||
export type RootStore = {
|
||||
UserStore: UserInfoState;
|
||||
BannerStore: BannerInfoState;
|
||||
|
||||
@@ -4,11 +4,10 @@ import { IMAGE_ROOT } from '@/shared/constants/common';
|
||||
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||
import { MenuCategory } from '@/entities/menu/ui/menu-category';
|
||||
import { FavoriteWrapper } from '@/entities/home/ui/favorite-wrapper';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import { useFavoriteEditOnStore, useMenuOnStore, useStore } from '@/shared/model/store';
|
||||
import { FilterMotionDuration, FilterMotionStyle, FilterMotionVariants, MenuItems } from '@/entities/common/model/constant';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { setHomeReloadKey } from '@/pages/home/home-page';
|
||||
import { useShortcutSaveMutation } from '@/entities/user/api/use-shortcut-save-mutation';
|
||||
import { ShortcutSaveParams, ShortcutSaveResponse } from '@/entities/user/model/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -28,17 +27,10 @@ interface ShortButton {
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface MenuProps {
|
||||
menuOn: boolean;
|
||||
setMenuOn: (menuOn: boolean) => void;
|
||||
favoriteEdit?: boolean;
|
||||
}
|
||||
export const Menu = () => {
|
||||
const { menuOn, setMenuOn } = useMenuOnStore();
|
||||
const { favoriteEditOn, setFavoriteEditOn } = useFavoriteEditOnStore();
|
||||
|
||||
export const Menu = ({
|
||||
menuOn,
|
||||
setMenuOn,
|
||||
favoriteEdit
|
||||
}: MenuProps) => {
|
||||
const { i18n } = useTranslation();
|
||||
const userMids = useStore.getState().UserStore.userMids;
|
||||
const userMid = useStore.getState().UserStore.mid;
|
||||
@@ -49,7 +41,6 @@ export const Menu = ({
|
||||
const { mutateAsync: shortcutSave } = useShortcutSaveMutation();
|
||||
|
||||
const [shortBtns, setShortBtns] = useState<ShortButton[]>([]);
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [changeMenuId, setChangeMenuId] = useState<string>();
|
||||
const [shortBtnIdx, setShortBtnIdx] = useState(0);
|
||||
|
||||
@@ -135,17 +126,12 @@ export const Menu = ({
|
||||
}, SCROLL_ANIMATION_DURATION);
|
||||
};
|
||||
const onClickToMenuClose = () => {
|
||||
if(editMode){
|
||||
setEditMode(false);
|
||||
if(favoriteEditOn){
|
||||
setFavoriteEditOn(false);
|
||||
callShortcutSave();
|
||||
|
||||
// 여기에 저장 로직?
|
||||
}
|
||||
else{
|
||||
setMenuOn(false);
|
||||
if(location.pathname === PATHS.home){
|
||||
setHomeReloadKey();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -160,8 +146,6 @@ export const Menu = ({
|
||||
iconFilePath={ MenuItems[i]?.iconFilePath }
|
||||
menuName={ MenuItems[i]?.menuName }
|
||||
subMenu={ MenuItems[i]?.subMenu }
|
||||
setMenuOn={ setMenuOn }
|
||||
editMode={ editMode }
|
||||
changeMenuId={ changeMenuId }
|
||||
setChangeMenuId= { setChangeMenuId }
|
||||
buttonRefs={ buttonRefs }
|
||||
@@ -255,11 +239,6 @@ export const Menu = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [i18n.language]);
|
||||
|
||||
useEffect(() => {
|
||||
if(favoriteEdit){
|
||||
setEditMode(favoriteEdit)
|
||||
}
|
||||
},[favoriteEdit]);
|
||||
|
||||
// 메뉴가 열릴 때 초기화
|
||||
useEffect(() => {
|
||||
@@ -292,7 +271,7 @@ export const Menu = ({
|
||||
<div className="full-menu-header">
|
||||
<div className="full-menu-title">{ userMid }</div>
|
||||
<div className="full-menu-actions">
|
||||
{ !editMode &&
|
||||
{ !favoriteEditOn &&
|
||||
<button
|
||||
className="full-menu-settings"
|
||||
onClick={ () => onClickToNavigate(PATHS.setting) }
|
||||
@@ -314,10 +293,7 @@ export const Menu = ({
|
||||
{
|
||||
<FavoriteWrapper
|
||||
usingType='menu'
|
||||
editMode={ editMode }
|
||||
setEditMode={ setEditMode }
|
||||
changeMenuId={ changeMenuId }
|
||||
setMenuOn={ setMenuOn }
|
||||
></FavoriteWrapper>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -8,17 +8,17 @@ import {
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAppBridge } from '@/hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useStore } from '@/shared/model/store';
|
||||
import { showAlert } from '../show-alert';
|
||||
import { checkGrant } from '@/shared/lib/check-grant';
|
||||
import { useFavoriteEditOnStore, useMenuOnStore } from '@/shared/model/store';
|
||||
|
||||
export const FooterNavigation = ({
|
||||
setMenuOn,
|
||||
footerCurrentPage,
|
||||
setFavoriteEdit
|
||||
}: FooterProps) => {
|
||||
const { favoriteEditOn, setFavoriteEditOn } = useFavoriteEditOnStore();
|
||||
const { navigate } = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const { menuOn, setMenuOn } = useMenuOnStore();
|
||||
|
||||
const [isFooterOn, setIsFooterOn] = useState<boolean>(true);
|
||||
|
||||
@@ -33,7 +33,7 @@ export const FooterNavigation = ({
|
||||
}
|
||||
};
|
||||
const onClickToOpenMenu = () => {
|
||||
setFavoriteEdit(false);
|
||||
setFavoriteEditOn(false);
|
||||
setMenuOn(true);
|
||||
};
|
||||
|
||||
|
||||
@@ -15,10 +15,7 @@ import { appBridge } from '@/utils/appBridge';
|
||||
export const HeaderNavigation = ({
|
||||
onBack,
|
||||
headerTitle,
|
||||
menuOn,
|
||||
headerType,
|
||||
setMenuOn,
|
||||
favoriteEdit,
|
||||
loginSuccess,
|
||||
mid,
|
||||
setMid
|
||||
@@ -95,11 +92,7 @@ export const HeaderNavigation = ({
|
||||
|| headerType === HeaderType.Alim
|
||||
|| headerType === HeaderType.LeftArrow
|
||||
) &&
|
||||
<Menu
|
||||
menuOn={ menuOn }
|
||||
setMenuOn={ setMenuOn }
|
||||
favoriteEdit={ favoriteEdit }
|
||||
></Menu>
|
||||
<Menu></Menu>
|
||||
}
|
||||
{
|
||||
(headerType !== HeaderType.NoHeader) &&
|
||||
|
||||
@@ -66,12 +66,9 @@ export const SubLayout = () => {
|
||||
|
||||
const [onBack, setOnBack] = useState(undefined);
|
||||
const [headerTitle, setHeaderTitle] = useState<string>('');
|
||||
const [isPullToRefreshEnabled, setIsPullToRefreshEnabled] = useState<boolean>(false);
|
||||
const [menuOn, setMenuOn] = useState<boolean>(false);
|
||||
const [headerType, setHeaderType] = useState<HeaderType>(HeaderType.NoHeader);
|
||||
const [footerMode, setFooterMode] = useState<boolean>(false);
|
||||
const [footerCurrentPage, setFooterCurrentPage] = useState<undefined | string | null>(undefined);
|
||||
const [favoriteEdit, setFavoriteEdit] = useState<boolean>(false);
|
||||
const [headerNavigationKey, setHeaderNavigationKey] = useState<number>(1);
|
||||
const [loginSuccess, setLoginSuccess] = useState<boolean>(false);
|
||||
const [mid, setMid] = useState<string>();
|
||||
@@ -411,10 +408,7 @@ export const SubLayout = () => {
|
||||
<HeaderNavigation
|
||||
onBack={ onBack }
|
||||
headerTitle={ headerTitle }
|
||||
menuOn={ menuOn }
|
||||
setMenuOn={ setMenuOn }
|
||||
headerType={ headerType }
|
||||
favoriteEdit={ favoriteEdit }
|
||||
key={ headerNavigationKey }
|
||||
loginSuccess={ loginSuccess }
|
||||
mid={ mid }
|
||||
@@ -425,21 +419,16 @@ export const SubLayout = () => {
|
||||
context={{
|
||||
setOnBack,
|
||||
setHeaderTitle,
|
||||
setIsPullToRefreshEnabled,
|
||||
setMenuOn,
|
||||
setHeaderType,
|
||||
setFooterMode,
|
||||
setFooterCurrentPage,
|
||||
setFavoriteEdit
|
||||
}}
|
||||
/>
|
||||
}
|
||||
{
|
||||
footerMode &&
|
||||
<FooterNavigation
|
||||
setMenuOn={ setMenuOn }
|
||||
footerCurrentPage={ footerCurrentPage }
|
||||
setFavoriteEdit={ setFavoriteEdit }
|
||||
></FooterNavigation>
|
||||
}
|
||||
{ alarmRoutesOn &&
|
||||
|
||||
@@ -28,24 +28,6 @@ export const useSetHeaderTitle = (title: string) => {
|
||||
return { setHeaderTitle };
|
||||
};
|
||||
|
||||
export const useSetIsPullToRefreshEnabled = (enabled: boolean) => {
|
||||
const { setIsPullToRefreshEnabled } = useSubLayoutContext();
|
||||
useEffect(() => {
|
||||
setIsPullToRefreshEnabled(enabled);
|
||||
return () => setIsPullToRefreshEnabled(false);
|
||||
}, [enabled, setIsPullToRefreshEnabled]);
|
||||
return { setIsPullToRefreshEnabled };
|
||||
};
|
||||
|
||||
export const useSetMenuOn = (menuOn: boolean) => {
|
||||
const { setMenuOn } = useSubLayoutContext();
|
||||
useEffect(() => {
|
||||
setMenuOn(menuOn);
|
||||
return () => setMenuOn(false);
|
||||
}, [menuOn, setMenuOn]);
|
||||
return { setMenuOn };
|
||||
};
|
||||
|
||||
export const useSetHeaderType = (headerType: HeaderType) => {
|
||||
const { setHeaderType } = useSubLayoutContext();
|
||||
useEffect(() => {
|
||||
@@ -55,15 +37,6 @@ export const useSetHeaderType = (headerType: HeaderType) => {
|
||||
return { setHeaderType };
|
||||
};
|
||||
|
||||
export const useSetFavoriteEdit = (favoriteEdit: boolean) => {
|
||||
const { setFavoriteEdit } = useSubLayoutContext();
|
||||
useEffect(() => {
|
||||
setFavoriteEdit(favoriteEdit);
|
||||
return () => setFavoriteEdit(false);
|
||||
}, [favoriteEdit, setFavoriteEdit]);
|
||||
return { setFavoriteEdit }
|
||||
};
|
||||
|
||||
export const useSetFooterMode = (footerMode: boolean) => {
|
||||
const { setFooterMode } = useSubLayoutContext();
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user