2021-12-31 23:58:59 +08:00
|
|
|
|
import clsx from "clsx";
|
|
|
|
|
import React, { useRef, useState } from "react";
|
|
|
|
|
import { ChromePicker } from "react-color";
|
2021-12-30 16:05:05 +08:00
|
|
|
|
import { usePagination } from "react-use-pagination";
|
2021-12-29 13:33:36 +08:00
|
|
|
|
|
2021-12-30 16:05:05 +08:00
|
|
|
|
import adapters from "../../static/adapters.json";
|
2021-12-31 23:58:59 +08:00
|
|
|
|
import { Tag, useFilteredObjs } from "../libs/store";
|
2021-12-30 22:46:15 +08:00
|
|
|
|
import Card from "./Card";
|
2021-12-31 23:58:59 +08:00
|
|
|
|
import Modal from "./Modal";
|
|
|
|
|
import ModalAction from "./ModalAction";
|
|
|
|
|
import ModalContent from "./ModalContent";
|
|
|
|
|
import ModalTitle from "./ModalTitle";
|
2021-12-30 16:05:05 +08:00
|
|
|
|
import Paginate from "./Paginate";
|
2021-12-31 23:58:59 +08:00
|
|
|
|
import TagComponent from "./Tag";
|
2021-12-30 16:05:05 +08:00
|
|
|
|
|
|
|
|
|
export default function Adapter(): JSX.Element {
|
2021-12-31 23:58:59 +08:00
|
|
|
|
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
2021-12-30 16:05:05 +08:00
|
|
|
|
const {
|
|
|
|
|
filter,
|
|
|
|
|
setFilter,
|
|
|
|
|
filteredObjs: filteredAdapters,
|
|
|
|
|
} = useFilteredObjs(adapters);
|
|
|
|
|
|
|
|
|
|
const props = usePagination({
|
|
|
|
|
totalItems: filteredAdapters.length,
|
|
|
|
|
initialPageSize: 10,
|
|
|
|
|
});
|
|
|
|
|
const { startIndex, endIndex } = props;
|
|
|
|
|
const currentAdapters = filteredAdapters.slice(startIndex, endIndex + 1);
|
|
|
|
|
|
2021-12-31 23:58:59 +08:00
|
|
|
|
const [form, setForm] = useState<{
|
|
|
|
|
name: string;
|
|
|
|
|
desc: string;
|
|
|
|
|
projectLink: string;
|
|
|
|
|
moduleName: string;
|
|
|
|
|
homepage: string;
|
|
|
|
|
}>({ name: "", desc: "", projectLink: "", moduleName: "", homepage: "" });
|
|
|
|
|
|
|
|
|
|
const ref = useRef<HTMLInputElement>(null);
|
|
|
|
|
const [tags, setTags] = useState<Tag[]>([]);
|
|
|
|
|
const [label, setLabel] = useState<string>("");
|
|
|
|
|
const [color, setColor] = useState<string>("#ea5252");
|
|
|
|
|
|
|
|
|
|
const onSubmit = () => {
|
|
|
|
|
setModalOpen(false);
|
|
|
|
|
const title = encodeURIComponent(`Adapter: ${form.name}`).replace(
|
|
|
|
|
/%2B/gi,
|
|
|
|
|
"+"
|
|
|
|
|
);
|
|
|
|
|
const body = encodeURIComponent(
|
|
|
|
|
`
|
|
|
|
|
**协议名称:**
|
|
|
|
|
|
|
|
|
|
${form.name}
|
|
|
|
|
|
|
|
|
|
**协议功能:**
|
|
|
|
|
|
|
|
|
|
${form.desc}
|
|
|
|
|
|
|
|
|
|
**PyPI 项目名:**
|
|
|
|
|
|
|
|
|
|
${form.projectLink}
|
|
|
|
|
|
|
|
|
|
**协议 import 包名:**
|
|
|
|
|
|
|
|
|
|
${form.moduleName}
|
|
|
|
|
|
|
|
|
|
**协议项目仓库/主页链接:**
|
|
|
|
|
|
|
|
|
|
${form.homepage}
|
|
|
|
|
|
|
|
|
|
**标签:**
|
|
|
|
|
|
|
|
|
|
${JSON.stringify(tags)}
|
|
|
|
|
`.trim()
|
|
|
|
|
).replace(/%2B/gi, "+");
|
|
|
|
|
window.open(
|
|
|
|
|
`https://github.com/nonebot/nonebot2/issues/new?title=${title}&body=${body}&labels=Adapter`
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
const onChange = (event) => {
|
|
|
|
|
const target = event.target;
|
|
|
|
|
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
|
|
|
const name = target.name;
|
|
|
|
|
|
|
|
|
|
setForm({
|
|
|
|
|
...form,
|
|
|
|
|
[name]: value,
|
|
|
|
|
});
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
};
|
|
|
|
|
const onChangeLabel = (event) => {
|
|
|
|
|
setLabel(event.target.value);
|
|
|
|
|
};
|
|
|
|
|
const onChangeColor = (color) => {
|
|
|
|
|
setColor(color.hex);
|
|
|
|
|
};
|
|
|
|
|
const validateTag = () => {
|
|
|
|
|
return label.length >= 1 && label.length <= 10;
|
|
|
|
|
};
|
|
|
|
|
const newTag = () => {
|
|
|
|
|
if (tags.length >= 3) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (validateTag()) {
|
|
|
|
|
const tag = { label, color };
|
|
|
|
|
setTags([...tags, tag]);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
const delTag = (index: number) => {
|
|
|
|
|
setTags(tags.filter((_, i) => i !== index));
|
|
|
|
|
};
|
|
|
|
|
const insertTagType = (text: string) => {
|
|
|
|
|
setLabel(text + label);
|
|
|
|
|
ref.current.value = text + label;
|
|
|
|
|
};
|
|
|
|
|
|
2021-12-30 16:05:05 +08:00
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4 px-4">
|
|
|
|
|
<input
|
|
|
|
|
className="w-full px-4 py-2 border rounded-full bg-light-nonepress-100 dark:bg-dark-nonepress-100"
|
|
|
|
|
value={filter}
|
|
|
|
|
placeholder="搜索适配器"
|
|
|
|
|
onChange={(event) => setFilter(event.target.value)}
|
|
|
|
|
/>
|
2021-12-31 23:58:59 +08:00
|
|
|
|
<button
|
|
|
|
|
className="w-full rounded-lg bg-hero text-white"
|
|
|
|
|
onClick={() => setModalOpen(true)}
|
|
|
|
|
>
|
2021-12-30 16:05:05 +08:00
|
|
|
|
发布适配器
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-1 p-4">
|
|
|
|
|
<Paginate {...props} />
|
|
|
|
|
</div>
|
2021-12-30 22:46:15 +08:00
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 px-4">
|
|
|
|
|
{currentAdapters.map((adapter, index) => (
|
2022-01-02 20:21:08 +08:00
|
|
|
|
<Card
|
|
|
|
|
key={index}
|
|
|
|
|
{...adapter}
|
|
|
|
|
action={`nb adapter install ${adapter.project_link}`}
|
|
|
|
|
actionDisabled={!adapter.project_link}
|
|
|
|
|
/>
|
2021-12-30 16:05:05 +08:00
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-1 p-4">
|
|
|
|
|
<Paginate {...props} />
|
|
|
|
|
</div>
|
2021-12-31 23:58:59 +08:00
|
|
|
|
<Modal active={modalOpen} setActive={setModalOpen}>
|
|
|
|
|
<ModalTitle title={"适配器信息"} />
|
|
|
|
|
<ModalContent>
|
|
|
|
|
<form onSubmit={onSubmit}>
|
|
|
|
|
<div className="grid grid-cols-1 gap-4 p-4">
|
|
|
|
|
<label className="flex flex-wrap">
|
|
|
|
|
<span className="mr-2">适配器名称:</span>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="name"
|
|
|
|
|
maxLength={20}
|
|
|
|
|
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
/>
|
|
|
|
|
</label>
|
|
|
|
|
<label className="flex flex-wrap">
|
|
|
|
|
<span className="mr-2">适配器介绍:</span>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="desc"
|
|
|
|
|
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
/>
|
|
|
|
|
</label>
|
|
|
|
|
<label className="flex flex-wrap">
|
|
|
|
|
<span className="mr-2">PyPI 项目名:</span>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="projectLink"
|
|
|
|
|
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
/>
|
|
|
|
|
</label>
|
|
|
|
|
<label className="flex flex-wrap">
|
|
|
|
|
<span className="mr-2">import 包名:</span>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="moduleName"
|
|
|
|
|
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
/>
|
|
|
|
|
</label>
|
|
|
|
|
<label className="flex flex-wrap">
|
|
|
|
|
<span className="mr-2">仓库/主页:</span>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
name="homepage"
|
|
|
|
|
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
|
|
|
|
|
onChange={onChange}
|
|
|
|
|
/>
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
<div className="px-4">
|
|
|
|
|
<label className="flex flex-wrap">
|
|
|
|
|
<span className="mr-2">标签:</span>
|
|
|
|
|
{tags.map((tag, index) => (
|
|
|
|
|
<TagComponent
|
|
|
|
|
key={index}
|
|
|
|
|
{...tag}
|
|
|
|
|
className="cursor-pointer"
|
|
|
|
|
onClick={() => delTag(index)}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="px-4 pt-4">
|
|
|
|
|
<input
|
|
|
|
|
ref={ref}
|
|
|
|
|
type="text"
|
|
|
|
|
className="px-2 flex-grow rounded bg-light-nonepress-200 dark:bg-dark-nonepress-200"
|
|
|
|
|
onChange={onChangeLabel}
|
|
|
|
|
/>
|
|
|
|
|
<ChromePicker
|
|
|
|
|
className="mt-2"
|
|
|
|
|
color={color}
|
|
|
|
|
disableAlpha={true}
|
|
|
|
|
onChangeComplete={onChangeColor}
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex flex-wrap mt-2 items-center">
|
|
|
|
|
<span className="mr-2">Type:</span>
|
|
|
|
|
<button
|
|
|
|
|
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
|
|
|
|
|
onClick={() => insertTagType("a:")}
|
|
|
|
|
>
|
|
|
|
|
Adapter
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
|
|
|
|
|
onClick={() => insertTagType("t:")}
|
|
|
|
|
>
|
|
|
|
|
Topic
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex mt-2">
|
|
|
|
|
<TagComponent label={label} color={color} />
|
|
|
|
|
<button
|
|
|
|
|
className={clsx(
|
|
|
|
|
"px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]",
|
|
|
|
|
{ "pointer-events-none opacity-60": !validateTag() }
|
|
|
|
|
)}
|
|
|
|
|
onClick={newTag}
|
|
|
|
|
>
|
|
|
|
|
添加标签
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</ModalContent>
|
|
|
|
|
<ModalAction>
|
|
|
|
|
<button
|
|
|
|
|
className="px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
|
|
|
|
|
onClick={() => setModalOpen(false)}
|
|
|
|
|
>
|
|
|
|
|
关闭
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
className="ml-2 px-2 h-9 min-w-[64px] rounded text-hero hover:bg-hero hover:bg-opacity-[.08]"
|
|
|
|
|
onClick={onSubmit}
|
|
|
|
|
>
|
|
|
|
|
发布
|
|
|
|
|
</button>
|
|
|
|
|
</ModalAction>
|
|
|
|
|
</Modal>
|
2021-12-30 16:05:05 +08:00
|
|
|
|
</>
|
|
|
|
|
);
|
2021-12-29 13:33:36 +08:00
|
|
|
|
}
|