finished most of the popup for file details and media
This commit is contained in:
@@ -41,11 +41,10 @@ export interface ContextMenuProps {
|
||||
Y: number;
|
||||
};
|
||||
folderMode?: boolean;
|
||||
quickItemMode: boolean;
|
||||
file?: FileInterface;
|
||||
folder?: FolderInterface;
|
||||
quickItemMode?: boolean;
|
||||
file?: FileInterface | null;
|
||||
folder?: FolderInterface | null;
|
||||
stopPropagation?: () => void;
|
||||
gridMode: boolean;
|
||||
}
|
||||
|
||||
const ContextMenu: React.FC<ContextMenuProps> = memo((props) => {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
import { memo, useCallback, useMemo } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks/store";
|
||||
import { downloadFileAPI } from "../../api/filesAPI";
|
||||
import CloseIcon from "../../icons/CloseIcon";
|
||||
import ActionsIcon from "../../icons/ActionsIcon";
|
||||
import { useContextMenu } from "../../hooks/contextMenu";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
import { resetPopupSelect } from "../../reducers/selected";
|
||||
import { getFileColor, getFileExtension } from "../../utils/files";
|
||||
import bytes from "bytes";
|
||||
import moment from "moment";
|
||||
|
||||
const FileInfoPopup = memo(() => {
|
||||
const file = useAppSelector((state) => state.selected.popupModal.file)!;
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
onContextMenu,
|
||||
closeContextMenu,
|
||||
onTouchStart,
|
||||
onTouchMove,
|
||||
onTouchEnd,
|
||||
clickStopPropagation,
|
||||
...contextMenuState
|
||||
} = useContextMenu();
|
||||
|
||||
const fileExtension = useMemo(
|
||||
() => getFileExtension(file.filename, 3),
|
||||
[file.filename]
|
||||
);
|
||||
|
||||
const imageColor = useMemo(
|
||||
() => getFileColor(file.filename),
|
||||
[file.filename]
|
||||
);
|
||||
|
||||
const formattedDate = useMemo(() => {
|
||||
return moment(file.uploadDate).format("L");
|
||||
}, [file.uploadDate, moment]);
|
||||
|
||||
const fileSize = useMemo(() => {
|
||||
return bytes(file.metadata.size);
|
||||
}, [file.metadata.size, bytes]);
|
||||
|
||||
const closePhotoViewer = useCallback(() => {
|
||||
dispatch(resetPopupSelect());
|
||||
}, [resetPopupSelect]);
|
||||
|
||||
const downloadItem = () => {
|
||||
downloadFileAPI(file._id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-screen h-screen bg-black bg-opacity-80 absolute top-0 left-0 right-0 bottom-0 z-50 flex justify-center items-center flex-col"
|
||||
id="outer-wrapper"
|
||||
>
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
quickItemMode={false}
|
||||
contextSelected={contextMenuState}
|
||||
closeContext={closeContextMenu}
|
||||
file={file}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="absolute top-[20px] flex justify-between w-full"
|
||||
id="actions-wrapper"
|
||||
>
|
||||
<div className="ml-4 flex items-center">
|
||||
<span className="inline-flex items-center mr-[15px] max-w-[27px] min-w-[27px] min-h-[27px] max-h-[27px]">
|
||||
<div
|
||||
className="h-[27px] w-[27px] bg-red-500 rounded-[3px] flex flex-row justify-center items-center"
|
||||
style={{ background: imageColor }}
|
||||
>
|
||||
<span className="font-semibold text-[9.5px] text-white">
|
||||
{fileExtension}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<p className="text-md text-white text-ellipsis overflow-hidden max-w-[200px] md:max-w-[600px] whitespace-nowrap">
|
||||
{file.filename}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex mr-4">
|
||||
<div onClick={onContextMenu} id="action-context-wrapper">
|
||||
<ActionsIcon
|
||||
className="pointer text-white w-[20px] h-[25px] mr-4"
|
||||
id="action-context-icon"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div onClick={closePhotoViewer} id="action-close-wrapper">
|
||||
<CloseIcon
|
||||
className="pointer text-white w-[25px] h-[25px]"
|
||||
id="action-close-icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[300px] p-4 bg-white rounded-md">
|
||||
<div className="mt-2 flex justify-between">
|
||||
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
|
||||
Type
|
||||
</span>
|
||||
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
|
||||
{fileExtension}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-between">
|
||||
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
|
||||
Size
|
||||
</span>
|
||||
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
|
||||
{fileSize}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-between">
|
||||
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
|
||||
Created
|
||||
</span>
|
||||
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
|
||||
{formattedDate}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-between">
|
||||
<span className="text-[#637381] text-[13px] font-normal leading-[20px] min-w-[50px]">
|
||||
Access
|
||||
</span>
|
||||
<span className="text-[#212b36] text-[13px] font-normal leading-[20px]">
|
||||
{file.metadata.link ? "Public" : "Private"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-[15px] flex justify-center">
|
||||
<a
|
||||
onClick={downloadItem}
|
||||
className="w-[80px] h-[40px] inline-flex items-center justify-center border border-[#3c85ee] rounded-[4px] text-[#3c85ee] text-[15px] font-medium no-underline animate mr-4 cursor-pointer hover:bg-[#f6f5fd]"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
<ActionsIcon
|
||||
onClick={onContextMenu}
|
||||
className="w-[20px] ml-4 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default FileInfoPopup;
|
||||
@@ -33,7 +33,7 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
|
||||
const multiSelectMode = useAppSelector(
|
||||
(state) => state.selected.multiSelectMode
|
||||
);
|
||||
const listView = useSelector((state) => state.filter.listView);
|
||||
const listView = useAppSelector((state) => state.filter.listView);
|
||||
const { image, hasThumbnail, imageOnError } = useThumbnail(
|
||||
file.metadata.hasThumbnail,
|
||||
file.metadata.thumbnailID
|
||||
@@ -148,7 +148,6 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
gridMode={false}
|
||||
quickItemMode={false}
|
||||
contextSelected={contextMenuState}
|
||||
closeContext={closeContextMenu}
|
||||
@@ -191,7 +190,6 @@ const FileItem: React.FC<FileItemProps> = memo((props) => {
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
gridMode={true}
|
||||
quickItemMode={false}
|
||||
contextSelected={contextMenuState}
|
||||
closeContext={closeContextMenu}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { disableListView, enableListView } from "../../actions/filter";
|
||||
import { useFiles } from "../../hooks/files";
|
||||
import { useUtils } from "../../hooks/utils";
|
||||
import SpinnerImage from "../SpinnerImage";
|
||||
import React, { memo, useMemo } from "react";
|
||||
import React, { memo } from "react";
|
||||
import FileItem from "../FileItem";
|
||||
import ParentBar from "../ParentBar";
|
||||
import { useParams } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks/store";
|
||||
import { toggleListView } from "../../reducers/filter";
|
||||
@@ -92,9 +88,9 @@ const Files = memo(() => {
|
||||
<div
|
||||
className={classNames(
|
||||
"grid grid-cols-[repeat(auto-fit,minmax(40%,45%))] xs:grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-[16px]",
|
||||
files?.pages[0]?.length > 1
|
||||
? "justify-center xs:justify-normal"
|
||||
: "justify-normal"
|
||||
files?.pages[0]?.length === 1
|
||||
? "justify-normal"
|
||||
: "justify-center xs:justify-normal"
|
||||
)}
|
||||
>
|
||||
{files?.pages.map((filePage, index) => (
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import React, { memo, useCallback, useMemo, useRef } from "react";
|
||||
import React, { memo, useCallback, useRef } from "react";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
import { useContextMenu } from "../../hooks/contextMenu";
|
||||
import classNames from "classnames";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { startSetSelectedItem } from "../../actions/selectedItem";
|
||||
import mobilecheck from "../../utils/mobileCheck";
|
||||
import moment from "moment";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks/store";
|
||||
@@ -84,14 +82,7 @@ const FolderItem: React.FC<FolderItemProps> = memo((props) => {
|
||||
|
||||
lastSelected.current = Date.now();
|
||||
},
|
||||
[
|
||||
startSetSelectedItem,
|
||||
mobilecheck,
|
||||
navigate,
|
||||
folder._id,
|
||||
elementSelected,
|
||||
multiSelectMode,
|
||||
]
|
||||
[mobilecheck, navigate, folder._id, elementSelected, multiSelectMode]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -111,7 +102,6 @@ const FolderItem: React.FC<FolderItemProps> = memo((props) => {
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
gridMode={true}
|
||||
folderMode={true}
|
||||
quickItemMode={folder.parent !== "/"}
|
||||
contextSelected={contextMenuState}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useFolders } from "../../hooks/folders";
|
||||
import FolderItem from "../FolderItem";
|
||||
import { memo, useCallback, useEffect } from "react";
|
||||
import ParentBar from "../ParentBar";
|
||||
import { memo, useCallback } from "react";
|
||||
import classNames from "classnames";
|
||||
import { useUtils } from "../../hooks/utils";
|
||||
import { useAppDispatch, useAppSelector } from "../../hooks/store";
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useUtils } from "../../hooks/utils";
|
||||
import Medias from "../Medias";
|
||||
import { useAppSelector } from "../../hooks/store";
|
||||
import PhotoViewerPopup from "../PhotoViewerPopup";
|
||||
import FileInfoPopup from "../FileInfoPopup";
|
||||
|
||||
const MainSection = memo(() => {
|
||||
const moverID = useAppSelector((state) => state.mover.id);
|
||||
@@ -25,6 +26,12 @@ const MainSection = memo(() => {
|
||||
<PhotoViewerPopup />
|
||||
) : undefined}
|
||||
|
||||
{selectedItem?.file &&
|
||||
!selectedItem.file.metadata.isVideo &&
|
||||
!selectedItem.file.metadata.hasThumbnail ? (
|
||||
<FileInfoPopup />
|
||||
) : undefined}
|
||||
|
||||
{moverID.length === 0 ? undefined : <MoverMenu />}
|
||||
|
||||
<div className="flex flex-row h-screen w-screen pt-16">
|
||||
|
||||
@@ -98,7 +98,6 @@ const MediaItem: React.FC<MediaItemType> = memo(({ file }) => {
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
gridMode={true}
|
||||
quickItemMode={false}
|
||||
contextSelected={contextMenuState}
|
||||
closeContext={closeContextMenu}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import env from "../../enviroment/envFrontEnd";
|
||||
// import { history } from "../../routers/AppRouter";
|
||||
import ParentBar from "./ParentBar";
|
||||
import withNavigate from "../HocComponent";
|
||||
|
||||
class ParentBarContainer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
onFolderClick = () => {
|
||||
const id =
|
||||
this.props.parentList.length !== 0
|
||||
? this.props.parentList[this.props.parentList.length - 1]
|
||||
: "";
|
||||
const url =
|
||||
env.uploadMode === "drive"
|
||||
? `/folder-google/${id}`
|
||||
: env.uploadMode === "s3"
|
||||
? `/folder-personal/${id}`
|
||||
: `/folder/${id}`;
|
||||
|
||||
if (id.length === 0) return;
|
||||
|
||||
this.props.navigate(url);
|
||||
};
|
||||
|
||||
homeClick = () => {
|
||||
this.props.navigate("/home");
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ParentBar
|
||||
homeClick={this.homeClick}
|
||||
onFolderClick={this.onFolderClick}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const connectStoreToProp = (state) => ({
|
||||
parentNameList: state.parent.parentNameList,
|
||||
parentList: state.parent.parentList,
|
||||
});
|
||||
|
||||
export default connect(connectStoreToProp)(withNavigate(ParentBarContainer));
|
||||
@@ -1,11 +1,10 @@
|
||||
import React, { memo, useMemo } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import React, { memo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useUtils } from "../../hooks/utils";
|
||||
import { useFolder } from "../../hooks/folders";
|
||||
import Spinner from "../Spinner";
|
||||
|
||||
const ParentBar = memo(() => {
|
||||
const { data: folder, isLoading } = useFolder();
|
||||
const { data: folder } = useFolder();
|
||||
console.log("folder", folder);
|
||||
const navigate = useNavigate();
|
||||
const { isHome, isTrash } = useUtils();
|
||||
@@ -16,6 +16,7 @@ import CircleRightIcon from "../../icons/CircleRightIcon";
|
||||
import { useFiles, useQuickFiles } from "../../hooks/files";
|
||||
import { FileInterface } from "../../types/file";
|
||||
import { InfiniteData } from "react-query";
|
||||
import { getFileColor, getFileExtension } from "../../utils/files";
|
||||
|
||||
const PhotoViewerPopup = memo(() => {
|
||||
const [image, setImage] = useState("");
|
||||
@@ -38,6 +39,16 @@ const PhotoViewerPopup = memo(() => {
|
||||
...contextMenuState
|
||||
} = useContextMenu();
|
||||
|
||||
const fileExtension = useMemo(
|
||||
() => getFileExtension(file.filename, 3),
|
||||
[file.filename]
|
||||
);
|
||||
|
||||
const imageColor = useMemo(
|
||||
() => getFileColor(file.filename),
|
||||
[file.filename]
|
||||
);
|
||||
|
||||
console.log("rerender");
|
||||
|
||||
const outOfBoundsClick = useCallback(
|
||||
@@ -229,7 +240,6 @@ const PhotoViewerPopup = memo(() => {
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
gridMode={true}
|
||||
quickItemMode={false}
|
||||
contextSelected={contextMenuState}
|
||||
closeContext={closeContextMenu}
|
||||
@@ -239,21 +249,38 @@ const PhotoViewerPopup = memo(() => {
|
||||
)}
|
||||
|
||||
<div
|
||||
className="absolute top-[20px] right-[20px] flex"
|
||||
className="absolute top-[20px] flex justify-between w-full"
|
||||
id="actions-wrapper"
|
||||
>
|
||||
<div onClick={onContextMenu} id="action-context-wrapper">
|
||||
<ActionsIcon
|
||||
className="pointer text-white w-[20px] h-[25px] mr-4"
|
||||
id="action-context-icon"
|
||||
/>
|
||||
<div className="ml-4 flex items-center">
|
||||
<span className="inline-flex items-center mr-[15px] max-w-[27px] min-w-[27px] min-h-[27px] max-h-[27px]">
|
||||
<div
|
||||
className="h-[27px] w-[27px] bg-red-500 rounded-[3px] flex flex-row justify-center items-center"
|
||||
style={{ background: imageColor }}
|
||||
>
|
||||
<span className="font-semibold text-[9.5px] text-white">
|
||||
{fileExtension}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<p className="text-md text-white text-ellipsis overflow-hidden max-w-[200px] md:max-w-[600px] whitespace-nowrap">
|
||||
{file.filename}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex mr-4">
|
||||
<div onClick={onContextMenu} id="action-context-wrapper">
|
||||
<ActionsIcon
|
||||
className="pointer text-white w-[20px] h-[25px] mr-4"
|
||||
id="action-context-icon"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div onClick={closePhotoViewer} id="action-close-wrapper">
|
||||
<CloseIcon
|
||||
className="pointer text-white w-[25px] h-[25px]"
|
||||
id="action-close-icon"
|
||||
/>
|
||||
<div onClick={closePhotoViewer} id="action-close-wrapper">
|
||||
<CloseIcon
|
||||
className="pointer text-white w-[25px] h-[25px]"
|
||||
id="action-close-icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex absolute pb-[70px] desktopMode:pb-0 top-[50px] bottom-0 w-full h-full justify-between items-end desktopMode:items-center p-4">
|
||||
|
||||
@@ -116,7 +116,6 @@ const QuickAccessItem = memo((props: QuickAccessItemProps) => {
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
gridMode={true}
|
||||
quickItemMode={true}
|
||||
contextSelected={contextMenuState}
|
||||
closeContext={closeContextMenu}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import bytes from "bytes";
|
||||
import moment from "moment";
|
||||
import React, { memo, useMemo } from "react";
|
||||
import { memo, useMemo } from "react";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
import classNames from "classnames";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { resetSelectedItem } from "../../actions/selectedItem";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { getFileExtension } from "../../utils/files";
|
||||
import { useContextMenu } from "../../hooks/contextMenu";
|
||||
import { setPopupFile } from "../../actions/popupFile";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAppSelector } from "../../hooks/store";
|
||||
import { resetSelected } from "../../reducers/selected";
|
||||
import { resetSelected, setPopupSelect } from "../../reducers/selected";
|
||||
import { useUtils } from "../../hooks/utils";
|
||||
|
||||
const RightSection = memo(() => {
|
||||
@@ -72,7 +70,7 @@ const RightSection = memo(() => {
|
||||
};
|
||||
const openItem = () => {
|
||||
if (selectedItem.file) {
|
||||
dispatch(setPopupFile({ showPopup: true, ...selectedItem.file }));
|
||||
dispatch(setPopupSelect({ type: "file", file: selectedItem.file }));
|
||||
} else if (!isTrash) {
|
||||
navigate(`/folder/${selectedItem.id}`);
|
||||
} else {
|
||||
@@ -163,14 +161,14 @@ const RightSection = memo(() => {
|
||||
</div>
|
||||
<div className="mt-[15px] flex items-center">
|
||||
<a
|
||||
className="w-[80px] h-[40px] inline-flex items-center justify-center border border-[#3c85ee] rounded-[4px] text-[#3c85ee] text-[15px] font-medium no-underline animate"
|
||||
className="w-[80px] h-[40px] inline-flex items-center justify-center border border-[#3c85ee] rounded-[4px] text-[#3c85ee] text-[15px] font-medium no-underline animate cursor-pointer hover:bg-[#f6f5fd]"
|
||||
onClick={openItem}
|
||||
>
|
||||
Open
|
||||
</a>
|
||||
<div className="ml-[15px] px-[20px]">
|
||||
<a
|
||||
className="w-[40px] h-[40px] rounded-[4px] inline-flex items-center justify-center border border-[#919eab] text-[#919eab] no-underline animate"
|
||||
className="w-[40px] h-[40px] rounded-[4px] inline-flex items-center justify-center border border-[#919eab] text-[#919eab] no-underline animate cursor-pointer"
|
||||
// @ts-ignore
|
||||
onClick={onContextMenu}
|
||||
>
|
||||
@@ -181,7 +179,6 @@ const RightSection = memo(() => {
|
||||
{contextMenuState.selected && (
|
||||
<div onClick={clickStopPropagation}>
|
||||
<ContextMenu
|
||||
gridMode={true}
|
||||
contextSelected={contextMenuState}
|
||||
closeContext={closeContextMenu}
|
||||
folderMode={!selectedItem.file}
|
||||
|
||||
@@ -26,7 +26,7 @@ const SearchBarItem = (props: SearchBarItemProps) => {
|
||||
if (type === "folder" && folder) {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center py-2 px-4 overflow-hidden text-ellipsis hover:bg-[#f6f5fd] cursor-pointer border-y mx-4"
|
||||
className="flex flex-row items-center p-2 overflow-hidden text-ellipsis hover:bg-[#f6f5fd] cursor-pointer border-y"
|
||||
key={folder._id}
|
||||
onClick={() => folderClick(folder)}
|
||||
>
|
||||
@@ -56,7 +56,7 @@ const SearchBarItem = (props: SearchBarItemProps) => {
|
||||
} else if (type === "file" && file) {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center py-2 px-4 overflow-hidden text-ellipsis hover:bg-[#f6f5fd] cursor-pointer border-y mx-4"
|
||||
className="flex flex-row items-center p-2 overflow-hidden text-ellipsis hover:bg-[#f6f5fd] cursor-pointer border-y"
|
||||
key={file._id}
|
||||
onClick={() => fileClick(file)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user