Skip to content

Commit

Permalink
feat: improve get download links
Browse files Browse the repository at this point in the history
  • Loading branch information
jmugliston committed Nov 12, 2024
1 parent d365a9d commit bd778f6
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 74 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "simple put.io",
"description": "Send download links directly to Put.io.",
"version": "2.2",
"version": "2.3",
"icons": {
"16": "icons/icon-16.png",
"19": "icons/icon-19.png",
Expand Down
15 changes: 1 addition & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
"@fortawesome/react-fontawesome": "^0.2.2",
"@putdotio/api-client": "^8.49.0",
"axios": "^1.7.7",
"copy-to-clipboard": "^3.3.3",
"pretty-checkbox": "^3.0.3",
"pretty-checkbox-react": "^3.2.0",
"react": "^18.3.1",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.3.1",
"react-modal": "^3.16.1",
"react-timeago": "^7.2.0",
Expand Down
103 changes: 54 additions & 49 deletions src/components/Files.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IFile } from "@putdotio/api-client";
import { Checkbox } from "pretty-checkbox-react";
import TimeAgo from "react-timeago";
import { Tooltip } from 'react-tooltip'
import { Tooltip } from "react-tooltip";

import ApiService from "../services/Api";
import Spinner from "./Loading";
import AddFolder from "./modals/AddFolder";
import { customTimeFormatter, formatBytes, truncate } from "../helpers";
import MoveFile from "./modals/MoveFile";
import GetDownloadLinks from "./modals/GetDownloadLinks";

import ApiService from "../services/Api";
import { customTimeFormatter, formatBytes, truncate } from "../helpers";

function Files({ api }: { api: ApiService }) {
const [initialLoad, setInitialLoad] = useState(true);
const [files, setFiles] = useState<IFile[]>([]);
const [breadcrumbs, setBreadcrumbs] = useState<IFile[]>([]);
const [selectedFileIds, setSelectedFileIds] = useState<number[]>([]);
const [fileLinks, setFileLinks] = useState<string[]>([]);

const [getDownloadLinksModalOpen, setGetDownloadLinksModalOpen] = useState(false);
const [addFolderModalOpen, setAddFolderModalOpen] = useState(false);
const [moveFileModalOpen, setMoveFileModalOpen] = useState(false);

Expand All @@ -39,7 +43,7 @@ function Files({ api }: { api: ApiService }) {

const selectFile = (file: IFile, checked: boolean) => {
setSelectedFileIds((prev) =>
checked ? [...prev, file.id] : prev.filter((id) => id !== file.id)
checked ? [...prev, file.id] : prev.filter((id) => id !== file.id),
);
};

Expand All @@ -63,7 +67,10 @@ function Files({ api }: { api: ApiService }) {
setFiles(files);
};

const runAction = async (action: () => Promise<unknown>, unselectOnFinish = true): Promise<unknown> => {
const runAction = async (
action: () => Promise<unknown>,
unselectOnFinish = true,
): Promise<unknown> => {
setActionError(null);
setActionInProgress(true);
let res = null;
Expand All @@ -78,7 +85,7 @@ function Files({ api }: { api: ApiService }) {
setActionInProgress(false);
refreshFileList();
}
return res
return res;
};

const navigateUp = async () => {
Expand Down Expand Up @@ -119,12 +126,20 @@ function Files({ api }: { api: ApiService }) {

return (
<div className="overflow-x-hidden" style={{ maxHeight: "425px" }}>
<GetDownloadLinks
closeModal={() => {
setGetDownloadLinksModalOpen(false);
setFileLinks([]);
}}
modalIsOpen={getDownloadLinksModalOpen}
links={fileLinks}
/>
<AddFolder
closeModal={async (folderName?: string) => {
setAddFolderModalOpen(false);
if (folderName && folderName !== "") {
await runAction(() =>
api.createFolder(folderName, currentFolderId)
api.createFolder(folderName, currentFolderId),
);
}
}}
Expand All @@ -135,7 +150,7 @@ function Files({ api }: { api: ApiService }) {
fileIds={selectedFileIds}
closeModal={async (folderId?: number) => {
setMoveFileModalOpen(false);
if (folderId) {
if (folderId !== undefined) {
setSelectedFileIds([]);
await runAction(() => api.moveFiles(selectedFileIds, folderId));
}
Expand Down Expand Up @@ -166,10 +181,11 @@ function Files({ api }: { api: ApiService }) {
<div className="text-base text-amber-500">
<Checkbox
color="warning"
checked={selectedFileIds.length === files.length}
onChange={(e) =>
selectAllFiles(e.currentTarget.checked)
checked={
selectedFileIds.length > 0 &&
selectedFileIds.length === files.length
}
onChange={(e) => selectAllFiles(e.currentTarget.checked)}
/>
</div>
<div>
Expand All @@ -183,10 +199,7 @@ function Files({ api }: { api: ApiService }) {
<Spinner />
</div>
)}
<Tooltip
id="tooltip-go-up"
content="Move up a directory"
/>
<Tooltip id="tooltip-go-up" content="Move up a directory" />
{currentFolderId !== 0 && (
<button
data-tooltip-id="tooltip-go-up"
Expand All @@ -197,10 +210,7 @@ function Files({ api }: { api: ApiService }) {
<FontAwesomeIcon icon={["fas", "turn-up"]} />
</button>
)}
<Tooltip
id="tooltip-delete"
content="Delete files"
/>
<Tooltip id="tooltip-delete" content="Delete files" />
<button
data-tooltip-id="tooltip-delete"
data-tooltip-content="Delete files"
Expand All @@ -214,10 +224,7 @@ function Files({ api }: { api: ApiService }) {
>
<FontAwesomeIcon icon={["far", "trash-can"]} />
</button>
<Tooltip
id="tooltip-zip"
content="Zip and download"
/>
<Tooltip id="tooltip-zip" content="Zip and download" />
<button
data-tooltip-id="tooltip-zip"
data-tooltip-content="Zip and download"
Expand All @@ -229,37 +236,38 @@ function Files({ api }: { api: ApiService }) {
>
<FontAwesomeIcon icon={["far", "file-zipper"]} />
</button>
<Tooltip
id="tooltip-copy-url"
content="Copy download URL(s)"
/>
<Tooltip id="tooltip-get-links" content="Get download links" />
<button
data-tooltip-id="tooltip-copy-url"
data-tooltip-content="Copy download URL(s)"
data-tooltip-id="tooltip-get-links"
data-tooltip-content="Get download links"
disabled={selectedFileIds.length === 0 || actionInProgress}
onClick={async () => {
const [fileIds, folderIds] = files.reduce((acc, file) => {
if (selectedFileIds.includes(file.id)) {
if (file.file_type === "FILE") {
acc[0].push(file.id);
}
if (file.file_type === "FOLDER") {
acc[1].push(file.id);
const [fileIds, folderIds] = files.reduce(
(acc, file) => {
if (selectedFileIds.includes(file.id)) {
if (file.file_type === "FILE") {
acc[0].push(file.id);
}
if (file.file_type === "FOLDER") {
acc[1].push(file.id);
}
}
}
return acc;
}, [[], []] as [number[], number[]]);
const urls = await runAction(() => api.getDownloadURLs(fileIds, folderIds), false)
navigator.clipboard.writeText((urls as string[]).join("\n"));
return acc;
},
[[], []] as [number[], number[]],
);
const urls = (await runAction(
() => api.getDownloadURLs(fileIds, folderIds),
false,
)) as string[];
setFileLinks(urls);
setGetDownloadLinksModalOpen(true);
}}
className="w-8 mx-1 py-2 border rounded border-gray-400 text-gray-700 bg-white hover:bg-gray-500 hover:text-white disabled:pointer-events-none disabled:border-gray-300 disabled:text-gray-300"
>
<FontAwesomeIcon icon={["fas", "link"]} />
</button>
<Tooltip
id="tooltip-move-folder"
content="Move file/folder"
/>
<Tooltip id="tooltip-move-folder" content="Move file/folder" />
<button
data-tooltip-id="tooltip-move-folder"
data-tooltip-content="Move file/folder"
Expand All @@ -269,10 +277,7 @@ function Files({ api }: { api: ApiService }) {
>
<FontAwesomeIcon icon={["fas", "circle-arrow-right"]} />
</button>
<Tooltip
id="tooltip-add-folder"
content="Add folder"
/>
<Tooltip id="tooltip-add-folder" content="Add folder" />
<button
data-tooltip-id="tooltip-add-folder"
data-tooltip-content="Add folder"
Expand Down
76 changes: 76 additions & 0 deletions src/components/modals/GetDownloadLinks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Modal from "react-modal";
import { Tooltip } from "react-tooltip";
import copy from "copy-to-clipboard";

Modal.setAppElement("#root");

function GetDownloadLinks({
closeModal,
modalIsOpen,
links,
}: {
closeModal: (folderName?: string) => void;
modalIsOpen: boolean;
links: string[];
}) {
return (
<Modal
isOpen={modalIsOpen}
onRequestClose={() => closeModal()}
style={{
content: {
top: "10%",
left: "10%",
right: "auto",
bottom: "auto",
marginRight: "-10%",
transform: "translate(-6%, -10%)",
width: "90%",
padding: 0,
},
overlay: {
zIndex: 999,
},
}}
contentLabel="Download Links"
>
<div>
<div className="flex items-center">
<h2 className="grow m-4 font-semibold text-2xl">Download Links</h2>
<button
className="mx-4 p-2 text-xs"
aria-description="close"
onClick={() => closeModal()}
>
<FontAwesomeIcon icon={["fas", "x"]} />
</button>
</div>
<hr />
<div className="m-8">
<div className="my-2 mx-1 ml-auto text-end">
<Tooltip id="tooltip-copy" openOnClick={true} content="Copied!" />
<button
data-tooltip-id="tooltip-copy"
data-tooltip-content="Copied!"
onClick={() => copy(links.join("\n"))}
>
<FontAwesomeIcon icon={["fas", "clipboard"]} />
</button>
</div>
<div className="flex">
<textarea
readOnly
rows={7}
id="links"
value={links.join("\n")}
className="rounded-none bg-white border text-gray-900 focus:outline-none focus:border-amber-300 focus:ring-1 focus:ring-amber-300 block flex-1 min-w-0 w-full text-sm border-gray-300 p-2.5"
/>
</div>
</div>
</div>
</Modal>
);
}

export default GetDownloadLinks;
1 change: 1 addition & 0 deletions src/components/modals/MoveFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ function MoveFile({
<button
onClick={() => {
const folder = currentFolderId;
console.log("folder", folder);
setBreadcrumbs([]);
closeModal(folder);
}}
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const customTimeFormatter = (
unit: string,
suffix: string,
epochSeconds: number,
nextFormatter: any
nextFormatter: any,
) => {
if (unit === "second" && value < 60) {
return "< 1 minute ago";
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/ApiToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function useApiToken() {
url.searchParams.append("response_type", "token");
url.searchParams.append(
"redirect_uri",
`https://${chrome.runtime.id}.chromiumapp.org/oauth`
`https://${chrome.runtime.id}.chromiumapp.org/oauth`,
);

chrome.identity.launchWebAuthFlow(
Expand All @@ -26,7 +26,7 @@ function useApiToken() {
const token = responseUrl.split("=")[1];
chrome.storage.sync.set({ accessToken: token });
setApiToken(token);
}
},
);
};

Expand Down
Loading

0 comments on commit bd778f6

Please sign in to comment.