Skip to content

Commit

Permalink
✨ add copy
Browse files Browse the repository at this point in the history
  • Loading branch information
StarHeartHunt committed Nov 21, 2023
1 parent 4fb8f17 commit 296abf8
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 31 deletions.
101 changes: 73 additions & 28 deletions website/src/components/Resource/DetailCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useEffect, useState } from "react";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
// @ts-expect-error: we need to make package have type: module
import copy from "copy-text-to-clipboard";

import { PyPIData } from "./types";

Expand All @@ -15,23 +17,62 @@ export type Props = {

export default function ResourceDetailCard({ resource }: Props) {
const [pypiData, setPypiData] = useState<PyPIData | null>(null);
const [copied, setCopied] = useState<boolean>(false);

const authorLink = `https://github.com/${resource.author}`;
const authorAvatar = `${authorLink}.png?size=100`;

const getProjectLink = (resource: Resource) => {
switch (resource.resourceType) {
case "plugin":
case "adapter":
case "driver":
return resource.project_link;
default:
return null;
}
};

const getModuleName = (resource: Resource) => {
switch (resource.resourceType) {
case "plugin":
case "adapter":
return resource.module_name;
case "driver":
return resource.module_name.replace(/~/, "nonebot.drivers.");
default:
return null;
}
};

const fetchPypiProject = (projectName: string) =>
fetch(`https://pypi.org/pypi/${projectName}/json`)
.then((response) => response.json())
.then((data) => setPypiData(data));

const copyCommand = (resource: Resource) => {
const projectLink = getProjectLink(resource);
if (projectLink) {
copy(`nb ${resource.resourceType} install ${projectLink}`);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};

useEffect(() => {
const fetchingTasks: Promise<void>[] = [];
if (resource.resourceType != "bot" && resource.project_link)
if (resource.resourceType === "bot" || resource.resourceType === "driver")
return;

if (resource.project_link)
fetchingTasks.push(fetchPypiProject(resource.project_link));

Promise.all(fetchingTasks);
}, [resource]);

const projectLink = getProjectLink(resource) || "无";
const moduleName = getModuleName(resource) || "无";

return (
<>
<div className="detail-card-header">
Expand All @@ -44,8 +85,11 @@ export default function ResourceDetailCard({ resource }: Props) {
<span className="detail-card-title-main">{resource.name}</span>
<span className="detail-card-title-sub">{resource.author}</span>
</div>
<button className="detail-card-copy-button detail-card-copy-button-desktop">
复制安装命令
<button
className="detail-card-copy-button detail-card-copy-button-desktop"
onClick={() => copyCommand(resource)}
>
{copied ? "复制成功" : "复制安装命令"}
</button>
</div>
<div className="detail-card-body">
Expand All @@ -61,43 +105,44 @@ export default function ResourceDetailCard({ resource }: Props) {
<div className="detail-card-body-right">
<div className="detail-card-meta-item">
<FontAwesomeIcon className="fa-fw" icon={["fas", "file-zipper"]} />{" "}
{pypiData &&
pypiData.releases[pypiData.info.version].reduce(
(acc, curr) => acc + curr.size,
0
) / 1000}
K
{(pypiData &&
pypiData.releases[pypiData.info.version] &&
`${
pypiData.releases[pypiData.info.version].reduce(
(acc, curr) => acc + curr.size,
0
) / 1000
}K`) ||
"无"}
</div>
<div className="detail-card-meta-item">
<span>
<FontAwesomeIcon
className="fa-fw"
icon={["fas", "scale-balanced"]}
/>{" "}
{pypiData && (pypiData.info.license || "无")}
{(pypiData && pypiData.info.license) || "无"}
</span>
</div>
<div className="detail-card-meta-item">
<FontAwesomeIcon className="fa-fw" icon={["fas", "tag"]} />{" "}
{pypiData && (pypiData.info.version || "无")}
{(pypiData && pypiData.info.version) || "无"}
</div>
{resource.resourceType === "plugin" && (
<div className="detail-card-meta-item">
<FontAwesomeIcon
className="fa-fw"
icon={["fas", "fingerprint"]}
/>{" "}
<span>{resource.module_name || "无"}</span>
</div>
)}
{resource.resourceType === "plugin" && (
<div className="detail-card-meta-item">
<FontAwesomeIcon className="fa-fw" icon={["fas", "cubes"]} />{" "}
<span>{resource.project_link || "无"}</span>
</div>
)}
<button className="detail-card-copy-button w-full">
复制安装命令

<div className="detail-card-meta-item">
<FontAwesomeIcon className="fa-fw" icon={["fas", "fingerprint"]} />{" "}
<span>{moduleName}</span>
</div>

<div className="detail-card-meta-item">
<FontAwesomeIcon className="fa-fw" icon={["fas", "cubes"]} />{" "}
<span>{projectLink}</span>
</div>
<button
className="detail-card-copy-button detail-card-copy-button-mobile w-full"
onClick={() => copyCommand(resource)}
>
{copied ? "复制成功" : "复制安装命令"}
</button>
</div>
</div>
Expand Down
6 changes: 5 additions & 1 deletion website/src/components/Resource/DetailCard/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
}

&-copy-button {
@apply ml-auto btn btn-sm lg:hidden;
@apply ml-auto btn btn-sm;

&-mobile {
@apply lg:hidden;
}

&-desktop {
@apply max-lg:hidden;
Expand Down
19 changes: 17 additions & 2 deletions website/src/components/Store/Content/Driver.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { usePagination } from "react-use-pagination";

import Admonition from "@theme/Admonition";

import Modal from "@/components/Modal";
import Paginate from "@/components/Paginate";
import ResourceCard from "@/components/Resource/Card";
import ResourceDetailCard from "@/components/Resource/DetailCard";
import Searcher from "@/components/Searcher";
import { authorFilter, tagFilter } from "@/libs/filter";
import { useSearchControl } from "@/libs/search";
Expand All @@ -19,6 +21,8 @@ export default function DriverPage(): JSX.Element {
const loading = drivers === null;

const [error, setError] = useState<Error | null>(null);
const [isOpenCardModal, setIsOpenCardModal] = useState<boolean>(false);
const [clickedDriver, setClickedDriver] = useState<Driver | null>(null);

const {
filteredResources: filteredDrivers,
Expand Down Expand Up @@ -59,8 +63,8 @@ export default function DriverPage(): JSX.Element {
}, []);

const onCardClick = useCallback((driver: Driver) => {
// TODO: open driver modal
console.log(driver, "clicked");
setClickedDriver(driver);
setIsOpenCardModal(true);
}, []);

const onCardTagClick = useCallback(
Expand Down Expand Up @@ -146,6 +150,17 @@ export default function DriverPage(): JSX.Element {
nextEnabled={nextEnabled}
previousEnabled={previousEnabled}
/>
{isOpenCardModal && (
<Modal
className="not-prose"
useCustomTitle
backdropExit
title="适配器详情"
setOpenModal={setIsOpenCardModal}
>
{clickedDriver && <ResourceDetailCard resource={clickedDriver} />}
</Modal>
)}
</>
);
}

0 comments on commit 296abf8

Please sign in to comment.