Copy playlist to clipboard, native UI (not alert)
This commit is contained in:
parent
e485844d86
commit
65e4f1b0a6
9 changed files with 135 additions and 24 deletions
|
|
@ -6,8 +6,9 @@ export const IconButton: React.FC<{
|
|||
className?: string;
|
||||
color?: string;
|
||||
children: any;
|
||||
href?: string;
|
||||
}> = (props) => {
|
||||
const { onClick, disabled, color, children, className, ...otherProps } =
|
||||
const { onClick, disabled, color, children, className, href, ...otherProps } =
|
||||
props;
|
||||
const onClickStopPropagation: MouseEventHandler<HTMLAnchorElement> = (e) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -22,7 +23,7 @@ export const IconButton: React.FC<{
|
|||
<a
|
||||
className={`p-1 text-blue-500 flex items-center justify-center ${colorClassName} ${className}`}
|
||||
onClick={onClickStopPropagation}
|
||||
href="#"
|
||||
href={href ?? "#"}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,13 @@ import { TorrentStats } from "../../api-types";
|
|||
import { APIContext, RefreshTorrentStatsContext } from "../../context";
|
||||
import { IconButton } from "./IconButton";
|
||||
import { DeleteTorrentModal } from "../modal/DeleteTorrentModal";
|
||||
import { FaCog, FaPause, FaPlay, FaTrash, FaClipboardList } from "react-icons/fa";
|
||||
import {
|
||||
FaCog,
|
||||
FaPause,
|
||||
FaPlay,
|
||||
FaTrash,
|
||||
FaClipboardList,
|
||||
} from "react-icons/fa";
|
||||
import { useErrorStore } from "../../stores/errorStore";
|
||||
|
||||
export const TorrentActions: React.FC<{
|
||||
|
|
@ -39,7 +45,7 @@ export const TorrentActions: React.FC<{
|
|||
text: `Error starting torrent id=${id}`,
|
||||
details: e,
|
||||
});
|
||||
},
|
||||
}
|
||||
)
|
||||
.finally(() => setDisabled(false));
|
||||
};
|
||||
|
|
@ -56,7 +62,7 @@ export const TorrentActions: React.FC<{
|
|||
text: `Error pausing torrent id=${id}`,
|
||||
details: e,
|
||||
});
|
||||
},
|
||||
}
|
||||
)
|
||||
.finally(() => setDisabled(false));
|
||||
};
|
||||
|
|
@ -71,6 +77,32 @@ export const TorrentActions: React.FC<{
|
|||
setDeleting(false);
|
||||
};
|
||||
|
||||
const playlistUrl = API.getPlaylistUrl(id);
|
||||
|
||||
const setAlert = useErrorStore((state) => state.setAlert);
|
||||
|
||||
const copyPlaylistUrlToClipboard = async () => {
|
||||
if (!playlistUrl) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(playlistUrl);
|
||||
} catch (e) {
|
||||
setCloseableError({
|
||||
text: "Error",
|
||||
details: { text: `Error copying playlist URL to clipboard: ${e}` },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setAlert({
|
||||
text: "Copied",
|
||||
details: {
|
||||
text: `Playlist URL copied to clipboard. Paste into e.g. VLC to play.`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full justify-center gap-2 dark:text-slate-300">
|
||||
{canUnpause && (
|
||||
|
|
@ -94,10 +126,11 @@ export const TorrentActions: React.FC<{
|
|||
<IconButton onClick={startDeleting} disabled={disabled}>
|
||||
<FaTrash className="hover:text-red-500" />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => {alert("Open this playlist link in external player like VLC")}}>
|
||||
<a target="_blank" href={"/torrents/"+id+"/playlist"}>
|
||||
<FaClipboardList className="hover:text-green-500"/>
|
||||
</a>
|
||||
<IconButton
|
||||
href={playlistUrl ?? "#"}
|
||||
onClick={copyPlaylistUrlToClipboard}
|
||||
>
|
||||
<FaClipboardList className="hover:text-green-500" />
|
||||
</IconButton>
|
||||
<DeleteTorrentModal id={id} show={deleting} onHide={cancelDeleting} />
|
||||
</div>
|
||||
|
|
|
|||
33
crates/librqbit/webui/src/components/modal/AlertModal.tsx
Normal file
33
crates/librqbit/webui/src/components/modal/AlertModal.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { ErrorWithLabel } from "../../rqbit-web";
|
||||
import { useErrorStore } from "../../stores/errorStore";
|
||||
import { Button } from "../buttons/Button";
|
||||
import { Modal } from "./Modal";
|
||||
import { ModalBody } from "./ModalBody";
|
||||
import { ModalFooter } from "./ModalFooter";
|
||||
|
||||
export const AlertModal: React.FC<{}> = () => {
|
||||
let alert = useErrorStore((store) => store.alert);
|
||||
let removeAlert = useErrorStore((store) => store.removeAlert);
|
||||
|
||||
if (alert) {
|
||||
return (
|
||||
<Modal isOpen={true} onClose={removeAlert} title={alert.text}>
|
||||
<ModalBody>
|
||||
{alert.details?.statusText && (
|
||||
<div className="pb-2 text-md">{alert.details?.statusText}</div>
|
||||
)}
|
||||
<div className="whitespace-pre-wrap text-sm">
|
||||
{alert.details?.text}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="cancel" onClick={removeAlert}>
|
||||
Close
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue