redesigned share menu

This commit is contained in:
subnub
2025-02-20 03:00:59 -05:00
parent de77ad412e
commit 7e5cf42d37
9 changed files with 144 additions and 98 deletions
+16 -1
View File
@@ -15,10 +15,11 @@ import Spinner from "../Spinner/Spinner";
import { useAppDispatch, useAppSelector } from "../../hooks/store";
import { useLocation, useParams } from "react-router-dom";
import classNames from "classnames";
import { useDragAndDrop } from "../../hooks/utils";
import { useDragAndDrop, useUtils } from "../../hooks/utils";
import MultiSelectBar from "../MultiSelectBar/MultiSelectBar";
import { useFolder, useFolders } from "../../hooks/folders";
import { removeNavigationMap } from "../../reducers/selected";
import AlertIcon from "../../icons/AlertIcon";
const DataForm = memo(
({ scrollDivRef }: { scrollDivRef: React.RefObject<HTMLDivElement> }) => {
@@ -42,6 +43,7 @@ const DataForm = memo(
const navigationMap = useAppSelector((state) => {
return state.selected.navigationMap[location.pathname];
});
const { isTrash } = useUtils();
const isLoading =
isLoadingFiles ||
@@ -114,6 +116,19 @@ const DataForm = memo(
<QuickAccess />
{isTrash && (
<div
className="bg-primary p-4 rounded-md text-sm
text-white mt-4 flex items-center select-none"
>
<AlertIcon className="w-6 h-6 min-h-6 min-w-6 mr-2" />
<span>
Items in the trash may be automatically deleted depending on
the servers settings
</span>
</div>
)}
<Folders scrollDivRef={scrollDivRef} />
<Files />
+1 -1
View File
@@ -24,7 +24,7 @@ const Header = () => {
return (
<header
id="header"
className="select-none border border-b fixed top-0 left-0 w-full bg-white z-10"
className="select-none border-b fixed top-0 left-0 w-full bg-white z-10"
>
<div className="px-6 flex justify-between min-h-16 items-center py-3.5">
<div className="items-center w-[260px] hidden desktopMode:flex">
+2 -2
View File
@@ -231,7 +231,7 @@ const MoverPopup = () => {
{folderList?.map((folder: FolderInterface) => (
<div
className={classNames(
"p-2 border-b border-[#ebe9f9] rounded-md mt-1 flex flex-row items-center",
"p-2 border-b border-[#ebe9f9] rounded-md flex flex-row items-center",
{
"bg-primary text-white hover:bg-primary-hover":
selectedFolder?._id === folder._id,
@@ -261,7 +261,7 @@ const MoverPopup = () => {
</div>
)}
</div>
<div className="mt-4">
<div className="mt-4 border border-gray-secondary rounded-md p-4">
<p className="text-sm overflow-hidden text-ellipsis whitespace-nowrap select-none">
{moveText}
</p>
+1
View File
@@ -50,6 +50,7 @@ const SearchBar = memo(() => {
const onSearch = (e: any) => {
e.preventDefault();
setShowSuggestions(false);
if (isMedia) {
if (searchText.length) {
navigate(`/search-media/${searchText}`);
+76 -94
View File
@@ -3,11 +3,6 @@ import CloseIcon from "../../icons/CloseIcon";
import { useAppDispatch, useAppSelector } from "../../hooks/store";
import { getFileColor, getFileExtension } from "../../utils/files";
import bytes from "bytes";
import {
makeOneTimePublicPopup,
makePublicPopup,
removeLinkPopup,
} from "../../popups/file";
import {
makeOneTimePublicAPI,
makePublicAPI,
@@ -21,11 +16,17 @@ import {
} from "../../reducers/selected";
import { toast } from "react-toastify";
import dayjs from "dayjs";
import LockIcon from "../../icons/LockIcon";
import OneIcon from "../../icons/OneIcon";
import PublicIcon from "../../icons/PublicIcon";
const SharePopup = memo(() => {
const file = useAppSelector((state) => state.selected.shareModal.file)!;
const [updating, setUpdating] = useState(false);
const [shareLink, setShareLink] = useState("");
const [shareType, setShareType] = useState<"private" | "public" | "one">(
"private"
);
const dispatch = useAppDispatch();
const { refetch: refetchFiles } = useFiles(false);
const { refetch: refetchQuickFiles } = useQuickFiles(false);
@@ -43,8 +44,6 @@ const SharePopup = memo(() => {
const makePublic = async () => {
try {
const result = await makePublicPopup();
if (!result) return;
setUpdating(true);
const { file: updatedFile } = await toast.promise(
makePublicAPI(file._id),
@@ -74,8 +73,6 @@ const SharePopup = memo(() => {
const makeOneTimePublic = async () => {
try {
const result = await makeOneTimePublicPopup();
if (!result) return;
setUpdating(true);
const { file: updatedFile } = await toast.promise(
makeOneTimePublicAPI(file._id),
@@ -105,8 +102,6 @@ const SharePopup = memo(() => {
const removeLink = async () => {
try {
const result = await removeLinkPopup();
if (!result) return;
setUpdating(true);
const updatedFile = await toast.promise(removeLinkAPI(file._id), {
pending: "Removing Link...",
@@ -133,6 +128,7 @@ const SharePopup = memo(() => {
};
const copyLink = () => {
if (shareType === "private") return;
navigator.clipboard.writeText(shareLink);
toast.success("Link Copied");
};
@@ -146,11 +142,47 @@ const SharePopup = memo(() => {
closeShareModal();
};
const permissionText = (() => {
if (shareType === "one") {
return `This file will be available for download one time,
after it is downloaded once the file will then automatically be marked as private.`;
} else if (shareType === "public") {
return "Anyone with the link can view and download this file";
} else {
return "Only you can view and download this file";
}
})();
const linkPreviewText = (() => {
if (shareType === "private") {
return "Document is private";
} else {
return shareLink;
}
})();
useEffect(() => {
if (!file.metadata.link) return;
const url = `${window.location.origin}/public-download/${file._id}/${file.metadata.link}`;
setShareLink(url);
}, [file._id, file.metadata.link]);
setShareType(file.metadata.linkType ? file.metadata.linkType : "private");
}, [file._id, file.metadata.link, file.metadata.linkType]);
const handleSelectChange = async (value: string) => {
if (value === "private") {
await removeLink();
} else if (value === "one") {
await makeOneTimePublic();
} else if (value === "public") {
await makePublic();
}
};
const selectOnChange = (e: any) => {
const value = e.target.value;
setShareType(value);
handleSelectChange(value);
};
return (
<div
@@ -183,90 +215,40 @@ const SharePopup = memo(() => {
</div>
</div>
</div>
<div className="w-[90%] sm:w-[400px] p-4 bg-white rounded-md">
<p className="text-lg mb-4 text-center">Share file</p>
<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 className="w-[90%] sm:w-[500px] p-6 bg-white rounded-md">
<p>Share file</p>
<div className="bg-light-primary p-6 rounded-md mt-4 flex items-center space-x-2">
<input
className="rounded-md w-full text-xs h-10 p-2"
value={linkPreviewText}
/>
<button
className="bg-primary text-white hover:bg-primary-hover text-xs w-20 p-1 py-3 rounded-md"
onClick={copyLink}
>
Copy link
</button>
</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>
<p className="mt-6">Permission</p>
<div className="flex mt-6 items-center">
{shareType === "private" && <LockIcon className="w-5 h-5 mr-2" />}
{shareType === "one" && <OneIcon className="w-5 h-5 mr-2" />}
{shareType === "public" && <PublicIcon className="w-5 h-5 mr-2" />}
<select
className="text-sm"
value={shareType}
onChange={selectOnChange}
disabled={updating}
>
<option value="private">Private</option>
<option value="public">Public</option>
<option value="one">Temporary</option>
</select>
{updating && (
<div className="border-t border-primary rounded-full animate-spin h-4 w-4 ml-2"></div>
)}
</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>
{shareLink && (
<div className="relative">
<input
placeholder="Share link"
className="w-full h-[48px] pl-[12px] pr-[53px] text-black border border-[#637381] rounded-[5px] outline-none text-[15px] mt-4"
value={shareLink}
readOnly={true}
/>
<div className="absolute right-0 top-0 bottom-0 flex items-center justify-center">
<a
className="text-[#3c85ee] text-[15px] font-medium no-underline mr-2 mt-4 cursor-pointer"
onClick={copyLink}
>
Copy
</a>
</div>
</div>
)}
{!shareLink && (
<div className="flex justify-between mt-4">
<button
className="py-2 px-4 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] disabled:opacity-50 disabled:cursor-not-allowed"
onClick={makePublic}
disabled={updating}
>
Share Indefinitely
</button>
<button
className="py-2 px-4 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] disabled:opacity-50 disabled:cursor-not-allowed"
onClick={makeOneTimePublic}
disabled={updating}
>
Single-Use Link
</button>
</div>
)}
{shareLink && (
<div className="flex justify-center mt-4">
<button
className="py-2 px-4 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] disabled:opacity-50 disabled:cursor-not-allowed"
onClick={removeLink}
disabled={updating}
>
Make Private
</button>
</div>
)}
<p className="text-xs mt-1.5 text-gray-500">{permissionText}</p>
</div>
</div>
);
+17
View File
@@ -0,0 +1,17 @@
type LockIconType = React.SVGAttributes<SVGSVGElement>;
const LockIcon: React.FC<LockIconType> = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
{...props}
fill="currentColor"
>
<title>lock-outline</title>
<path d="M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" />
</svg>
);
};
export default LockIcon;
+15
View File
@@ -0,0 +1,15 @@
const OneIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
{...props}
fill="currentColor"
>
<title>numeric-1-circle-outline</title>
<path d="M10,7H14V17H12V9H10V7M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" />
</svg>
);
};
export default OneIcon;
+15
View File
@@ -0,0 +1,15 @@
const PublicIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
{...props}
fill="currentColor"
>
<title>earth</title>
<path d="M17.9,17.39C17.64,16.59 16.89,16 16,16H15V13A1,1 0 0,0 14,12H8V10H10A1,1 0 0,0 11,9V7H13A2,2 0 0,0 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39M11,19.93C7.05,19.44 4,16.08 4,12C4,11.38 4.08,10.78 4.21,10.21L9,15V16A2,2 0 0,0 11,18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</svg>
);
};
export default PublicIcon;
+1
View File
@@ -14,6 +14,7 @@ module.exports = {
"gray-primary": "#637381",
"gray-secondary": "#e8eef2",
"gray-third": "#ebe9f9",
"light-primary": "rgba(60, 133, 238, 0.4)",
},
},
screens: {