mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 04:17:23 +00:00
Add kun patch.
This commit is contained in:
BIN
frontend/public/kun.webp
Normal file
BIN
frontend/public/kun.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
@@ -67,7 +67,7 @@ class App {
|
|||||||
getPreFetchData() {
|
getPreFetchData() {
|
||||||
const preFetchDataElement = document.getElementById("pre_fetch_data");
|
const preFetchDataElement = document.getElementById("pre_fetch_data");
|
||||||
if (preFetchDataElement) {
|
if (preFetchDataElement) {
|
||||||
let content = preFetchDataElement.textContent
|
let content = preFetchDataElement.textContent;
|
||||||
if (!content) {
|
if (!content) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -87,8 +87,8 @@ export default function Navigator() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FloatingToTopButton />
|
<FloatingToTopButton />
|
||||||
<div className="z-1 fixed top-0 w-full backdrop-blur h-16"/>
|
<div className="z-1 fixed top-0 w-full backdrop-blur h-16" />
|
||||||
<div className="z-2 fixed top-0 w-full h-16 bg-base-100 opacity-80"/>
|
<div className="z-2 fixed top-0 w-full h-16 bg-base-100 opacity-80" />
|
||||||
<div
|
<div
|
||||||
className="navbar shadow-sm fixed top-0 z-3 lg:z-10 bg-transparent h-16"
|
className="navbar shadow-sm fixed top-0 z-3 lg:z-10 bg-transparent h-16"
|
||||||
key={key}
|
key={key}
|
||||||
|
@@ -20,7 +20,11 @@ export default function ResourceCard({ resource }: { resource: Resource }) {
|
|||||||
navigate(`/resources/${resource.id}`);
|
navigate(`/resources/${resource.id}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={"card shadow hover:shadow-md transition-shadow bg-base-100-tr82"}>
|
<div
|
||||||
|
className={
|
||||||
|
"card shadow hover:shadow-md transition-shadow bg-base-100-tr82"
|
||||||
|
}
|
||||||
|
>
|
||||||
{resource.image != null && (
|
{resource.image != null && (
|
||||||
<figure>
|
<figure>
|
||||||
<img
|
<img
|
||||||
|
@@ -235,6 +235,7 @@ export const i18nData = {
|
|||||||
"Posted a comment": "Posted a comment",
|
"Posted a comment": "Posted a comment",
|
||||||
"Resources": "Resources",
|
"Resources": "Resources",
|
||||||
"Added a new file": "Added a new file",
|
"Added a new file": "Added a new file",
|
||||||
|
"Data from": "Data from",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zh-CN": {
|
"zh-CN": {
|
||||||
@@ -463,6 +464,8 @@ export const i18nData = {
|
|||||||
|
|
||||||
"Resources": "资源",
|
"Resources": "资源",
|
||||||
"Added a new file": "添加了新文件",
|
"Added a new file": "添加了新文件",
|
||||||
|
|
||||||
|
"Data from": "数据来源",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zh-TW": {
|
"zh-TW": {
|
||||||
@@ -691,6 +694,8 @@ export const i18nData = {
|
|||||||
|
|
||||||
"Resources": "資源",
|
"Resources": "資源",
|
||||||
"Added a new file": "添加了新檔案",
|
"Added a new file": "添加了新檔案",
|
||||||
|
|
||||||
|
"Data from": "數據來源",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -315,4 +315,4 @@ body {
|
|||||||
|
|
||||||
.bg-base-100-tr82 {
|
.bg-base-100-tr82 {
|
||||||
background-color: rgb(var(--color-base-100-rgb) / 0.82);
|
background-color: rgb(var(--color-base-100-rgb) / 0.82);
|
||||||
}
|
}
|
||||||
|
170
frontend/src/network/kun.ts
Normal file
170
frontend/src/network/kun.ts
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { Response } from "./models.ts";
|
||||||
|
|
||||||
|
const KunApi = {
|
||||||
|
isAvailable(): boolean {
|
||||||
|
return (
|
||||||
|
window.location.hostname === "res.nyne.dev" ||
|
||||||
|
window.location.hostname.startsWith("localhost")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getPatch(id: string): Promise<Response<KunPatchResponse>> {
|
||||||
|
try {
|
||||||
|
const client = axios.create({
|
||||||
|
validateStatus(status) {
|
||||||
|
return status === 200 || status === 404; // Accept only 200 and 404 responses
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const res = await client.get(
|
||||||
|
`https://www.moyu.moe/api/hikari?vndb_id=${id}`,
|
||||||
|
);
|
||||||
|
if (res.status === 404) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "404",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw new Error(`HTTP error! status: ${res.status}`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "ok",
|
||||||
|
data: res.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching files:", error);
|
||||||
|
return { success: false, message: "Failed to fetch files" };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KunApi;
|
||||||
|
|
||||||
|
export interface KunUser {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KunPatchResponse {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
// e.g. "vndb_id": "v19658",
|
||||||
|
vndb_id: string;
|
||||||
|
banner: string;
|
||||||
|
introduction: string;
|
||||||
|
// e.g. "released": "2016-11-25",
|
||||||
|
released: string;
|
||||||
|
status: number;
|
||||||
|
download: number;
|
||||||
|
view: number;
|
||||||
|
resource_update_time: Date;
|
||||||
|
type: string[];
|
||||||
|
language: string[];
|
||||||
|
engine: string[];
|
||||||
|
platform: string[];
|
||||||
|
user_id: number;
|
||||||
|
user: KunUser;
|
||||||
|
created: Date;
|
||||||
|
updated: Date;
|
||||||
|
resource: KunPatchResourceResponse[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KunPatchResourceResponse {
|
||||||
|
id: number;
|
||||||
|
storage: "s3" | "user";
|
||||||
|
name: string;
|
||||||
|
model_name: string;
|
||||||
|
size: string;
|
||||||
|
code: string;
|
||||||
|
password: string;
|
||||||
|
note: string;
|
||||||
|
hash: string;
|
||||||
|
type: string[];
|
||||||
|
language: string[];
|
||||||
|
platform: string[];
|
||||||
|
download: number;
|
||||||
|
status: number;
|
||||||
|
update_time: Date;
|
||||||
|
user_id: number;
|
||||||
|
patch_id: number;
|
||||||
|
created: Date;
|
||||||
|
user: KunUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HikariResponse {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data: KunPatchResponse | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SUPPORTED_LANGUAGE_MAP: Record<string, string> = {
|
||||||
|
"zh-Hans": "简体中文",
|
||||||
|
"zh-Hant": "繁體中文",
|
||||||
|
"ja": "日本語",
|
||||||
|
"en": "English",
|
||||||
|
"other": "其它",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function kunLanguageToString(language: string): string {
|
||||||
|
return SUPPORTED_LANGUAGE_MAP[language] || language;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SUPPORTED_PLATFORM_MAP: Record<string, string> = {
|
||||||
|
windows: "Windows",
|
||||||
|
android: "Android",
|
||||||
|
macos: "MacOS",
|
||||||
|
ios: "iOS",
|
||||||
|
linux: "Linux",
|
||||||
|
other: "其它",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function kunPlatformToString(platform: string): string {
|
||||||
|
return SUPPORTED_PLATFORM_MAP[platform] || platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resourceTypes = [
|
||||||
|
{
|
||||||
|
value: "manual",
|
||||||
|
label: "人工翻译补丁",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "ai",
|
||||||
|
label: "AI 翻译补丁",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "machine_polishing",
|
||||||
|
label: "机翻润色",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "machine",
|
||||||
|
label: "机翻补丁",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "save",
|
||||||
|
label: "全 CG 存档",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "crack",
|
||||||
|
label: "破解补丁",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "fix",
|
||||||
|
label: "修正补丁",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "mod",
|
||||||
|
label: "魔改补丁",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "other",
|
||||||
|
label: "其它",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function kunResourceTypeToString(type: string): string {
|
||||||
|
const resourceType = resourceTypes.find((t) => t.value === type);
|
||||||
|
return resourceType ? resourceType.label : type;
|
||||||
|
}
|
@@ -50,6 +50,13 @@ import { BiLogoSteam } from "react-icons/bi";
|
|||||||
import { CommentTile } from "../components/comment_tile.tsx";
|
import { CommentTile } from "../components/comment_tile.tsx";
|
||||||
import { CommentInput } from "../components/comment_input.tsx";
|
import { CommentInput } from "../components/comment_input.tsx";
|
||||||
import { useNavigator } from "../components/navigator.tsx";
|
import { useNavigator } from "../components/navigator.tsx";
|
||||||
|
import KunApi, {
|
||||||
|
kunLanguageToString,
|
||||||
|
KunPatchResourceResponse,
|
||||||
|
KunPatchResponse,
|
||||||
|
kunPlatformToString,
|
||||||
|
kunResourceTypeToString,
|
||||||
|
} from "../network/kun.ts";
|
||||||
|
|
||||||
export default function ResourcePage() {
|
export default function ResourcePage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -63,6 +70,8 @@ export default function ResourcePage() {
|
|||||||
|
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
|
|
||||||
|
const [visitedTabs, setVisitedTabs] = useState<Set<number>>(new Set([]));
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const navigator = useNavigator();
|
const navigator = useNavigator();
|
||||||
@@ -113,10 +122,12 @@ export default function ResourcePage() {
|
|||||||
if (resource) {
|
if (resource) {
|
||||||
document.title = resource.title;
|
document.title = resource.title;
|
||||||
if (resource.images.length > 0) {
|
if (resource.images.length > 0) {
|
||||||
navigator.setBackground(network.getResampledImageUrl(resource.images[0].id));
|
navigator.setBackground(
|
||||||
|
network.getResampledImageUrl(resource.images[0].id),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [resource])
|
}, [resource]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@@ -136,6 +147,7 @@ export default function ResourcePage() {
|
|||||||
// 初始状态读取hash
|
// 初始状态读取hash
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPage(getPageFromHash(window.location.hash));
|
setPage(getPageFromHash(window.location.hash));
|
||||||
|
setVisitedTabs(new Set([getPageFromHash(window.location.hash)]));
|
||||||
// 监听hash变化
|
// 监听hash变化
|
||||||
const onHashChange = () => {
|
const onHashChange = () => {
|
||||||
setPage(getPageFromHash(window.location.hash));
|
setPage(getPageFromHash(window.location.hash));
|
||||||
@@ -149,6 +161,8 @@ export default function ResourcePage() {
|
|||||||
const handleTabChange = (idx: number) => {
|
const handleTabChange = (idx: number) => {
|
||||||
setPage(idx);
|
setPage(idx);
|
||||||
setHashByPage(idx);
|
setHashByPage(idx);
|
||||||
|
// Mark tab as visited when switched to
|
||||||
|
setVisitedTabs((prev) => new Set(prev).add(idx));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
@@ -222,9 +236,12 @@ export default function ResourcePage() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="tabs tabs-box my-4 mx-2 p-4 shadow" style={{
|
<div
|
||||||
backgroundColor: "rgb(var(--color-base-100-rgb) / 0.82)",
|
className="tabs tabs-box my-4 mx-2 p-4 shadow"
|
||||||
}}>
|
style={{
|
||||||
|
backgroundColor: "rgb(var(--color-base-100-rgb) / 0.82)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<label className="tab transition-all">
|
<label className="tab transition-all">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
@@ -236,7 +253,7 @@ export default function ResourcePage() {
|
|||||||
<span className="text-sm">{t("Description")}</span>
|
<span className="text-sm">{t("Description")}</span>
|
||||||
</label>
|
</label>
|
||||||
<div key={"article"} className="tab-content p-2">
|
<div key={"article"} className="tab-content p-2">
|
||||||
<Article resource={resource} />
|
{visitedTabs.has(0) && <Article resource={resource} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className="tab transition-all">
|
<label className="tab transition-all">
|
||||||
@@ -250,7 +267,9 @@ export default function ResourcePage() {
|
|||||||
<span className="text-sm">{t("Files")}</span>
|
<span className="text-sm">{t("Files")}</span>
|
||||||
</label>
|
</label>
|
||||||
<div key={"files"} className="tab-content p-2">
|
<div key={"files"} className="tab-content p-2">
|
||||||
<Files files={resource.files} resourceID={resource.id} />
|
{visitedTabs.has(1) && (
|
||||||
|
<Files files={resource.files} resource={resource} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className="tab transition-all">
|
<label className="tab transition-all">
|
||||||
@@ -271,7 +290,7 @@ export default function ResourcePage() {
|
|||||||
) : null}
|
) : null}
|
||||||
</label>
|
</label>
|
||||||
<div key={"comments"} className="tab-content p-2">
|
<div key={"comments"} className="tab-content p-2">
|
||||||
<Comments resourceId={resource.id} />
|
{visitedTabs.has(2) && <Comments resourceId={resource.id} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={"grow"}></div>
|
<div className={"grow"}></div>
|
||||||
@@ -315,11 +334,15 @@ function Tags({ tags }: { tags: Tag[] }) {
|
|||||||
<>
|
<>
|
||||||
{Array.from(tagsMap.entries()).map(([type, tags]) => (
|
{Array.from(tagsMap.entries()).map(([type, tags]) => (
|
||||||
<p key={type} className={"px-4"}>
|
<p key={type} className={"px-4"}>
|
||||||
<Badge className="shadow-xs" key={type}>{type == "" ? t("Other") : type}</Badge>
|
<Badge className="shadow-xs" key={type}>
|
||||||
|
{type == "" ? t("Other") : type}
|
||||||
|
</Badge>
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<Badge
|
<Badge
|
||||||
key={tag.name}
|
key={tag.name}
|
||||||
className={"m-1 cursor-pointer badge-soft badge-primary shadow-xs"}
|
className={
|
||||||
|
"m-1 cursor-pointer badge-soft badge-primary shadow-xs"
|
||||||
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/tag/${tag.name}`);
|
navigate(`/tag/${tag.name}`);
|
||||||
}}
|
}}
|
||||||
@@ -603,7 +626,7 @@ function RelatedResourceCard({
|
|||||||
height: imgHeight,
|
height: imgHeight,
|
||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
}}
|
}}
|
||||||
className={"h-full min-h-0 object-cover min-w-0"}
|
className={"h-full object-cover min-w-0"}
|
||||||
alt={"cover"}
|
alt={"cover"}
|
||||||
src={network.getImageUrl(r.image?.id)}
|
src={network.getImageUrl(r.image?.id)}
|
||||||
/>
|
/>
|
||||||
@@ -702,32 +725,33 @@ function FileTile({ file }: { file: RFile }) {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={"flex flex-row items-center"}>
|
<div className={"flex flex-row items-center"}>
|
||||||
{
|
{file.size > 10 * 1024 * 1024 ? (
|
||||||
file.size > 10 * 1024 * 1024 ? (
|
<button
|
||||||
<button
|
ref={buttonRef}
|
||||||
ref={buttonRef}
|
className={"btn btn-primary btn-soft btn-square"}
|
||||||
className={"btn btn-primary btn-soft btn-square"}
|
onClick={() => {
|
||||||
onClick={() => {
|
if (!app.cloudflareTurnstileSiteKey) {
|
||||||
if (!app.cloudflareTurnstileSiteKey) {
|
const link = network.getFileDownloadLink(file.id, "");
|
||||||
const link = network.getFileDownloadLink(file.id, "");
|
window.open(link, "_blank");
|
||||||
window.open(link, "_blank");
|
} else {
|
||||||
} else {
|
showPopup(
|
||||||
showPopup(<CloudflarePopup file={file} />, buttonRef.current!);
|
<CloudflarePopup file={file} />,
|
||||||
}
|
buttonRef.current!,
|
||||||
}}
|
);
|
||||||
>
|
}
|
||||||
<MdOutlineDownload size={24} />
|
}}
|
||||||
</button>
|
>
|
||||||
) : (
|
<MdOutlineDownload size={24} />
|
||||||
<a
|
</button>
|
||||||
href={network.getFileDownloadLink(file.id, "")}
|
) : (
|
||||||
target="_blank"
|
<a
|
||||||
className={"btn btn-primary btn-soft btn-square"}
|
href={network.getFileDownloadLink(file.id, "")}
|
||||||
>
|
target="_blank"
|
||||||
<MdOutlineDownload size={24} />
|
className={"btn btn-primary btn-soft btn-square"}
|
||||||
</a>
|
>
|
||||||
)
|
<MdOutlineDownload size={24} />
|
||||||
}
|
</a>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -777,7 +801,13 @@ function CloudflarePopup({ file }: { file: RFile }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Files({ files, resourceID }: { files: RFile[]; resourceID: number }) {
|
function Files({
|
||||||
|
files,
|
||||||
|
resource,
|
||||||
|
}: {
|
||||||
|
files: RFile[];
|
||||||
|
resource: ResourceDetails;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={"pt-3"}>
|
<div className={"pt-3"}>
|
||||||
{files.map((file) => {
|
{files.map((file) => {
|
||||||
@@ -786,9 +816,10 @@ function Files({ files, resourceID }: { files: RFile[]; resourceID: number }) {
|
|||||||
<div className={"h-2"}></div>
|
<div className={"h-2"}></div>
|
||||||
{(app.canUpload() || app.allowNormalUserUpload) && (
|
{(app.canUpload() || app.allowNormalUserUpload) && (
|
||||||
<div className={"flex flex-row-reverse"}>
|
<div className={"flex flex-row-reverse"}>
|
||||||
<CreateFileDialog resourceId={resourceID}></CreateFileDialog>
|
<CreateFileDialog resourceId={resource.id}></CreateFileDialog>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<KunFiles resource={resource} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1412,3 +1443,138 @@ function DeleteFileDialog({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function KunFiles({ resource }: { resource: ResourceDetails }) {
|
||||||
|
let vnid = "";
|
||||||
|
for (const link of resource.links) {
|
||||||
|
if (link.label.toLowerCase() === "vndb") {
|
||||||
|
vnid = link.url.split("/").pop() || "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [data, setData] = useState<KunPatchResponse | null>(null);
|
||||||
|
|
||||||
|
const [isLoading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!vnid || !KunApi.isAvailable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KunApi.getPatch(vnid).then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
setData(res.data!);
|
||||||
|
} else if (res.message === "404") {
|
||||||
|
// ignore
|
||||||
|
} else {
|
||||||
|
setError(res.message);
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}, [vnid]);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <ErrorAlert className={"my-2"} message={error} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vnid || !KunApi.isAvailable() || data === null) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="mx-2 my-4 flex">
|
||||||
|
<a href="https://moyu.moe" target="_blank">
|
||||||
|
<div className="border-b-2 pb-1 border-transparent hover:border-primary select-none cursor-pointer transition-all flex items-center gap-2">
|
||||||
|
<img src="/kun.webp" className="h-8 w-8 rounded-full" />
|
||||||
|
<span className="text-xl font-bold">鲲补丁</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{data && (
|
||||||
|
<div className={"flex flex-col gap-2"}>
|
||||||
|
{data.resource.map((file) => {
|
||||||
|
return <KunFile file={file} patchID={data.id} key={file.id} />;
|
||||||
|
})}
|
||||||
|
{data.resource.length === 0 && (
|
||||||
|
<p className={"text-sm text-base-content/80"}>
|
||||||
|
{t("No patches found for this VN.")}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function KunFile({
|
||||||
|
file,
|
||||||
|
patchID,
|
||||||
|
}: {
|
||||||
|
file: KunPatchResourceResponse;
|
||||||
|
patchID: number;
|
||||||
|
}) {
|
||||||
|
const tags: string[] = [];
|
||||||
|
if (file.model_name) {
|
||||||
|
tags.push(file.model_name);
|
||||||
|
}
|
||||||
|
tags.push(...file.platform.map((p) => kunPlatformToString(p)));
|
||||||
|
tags.push(...file.language.map((l) => kunLanguageToString(l)));
|
||||||
|
tags.push(...file.type.map((t) => kunResourceTypeToString(t)));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"card shadow bg-base-100 mb-4"}>
|
||||||
|
<div className={"p-4 flex flex-row items-center"}>
|
||||||
|
<div className={"grow"}>
|
||||||
|
<h4 className={"font-bold break-all"}>{file.name}</h4>
|
||||||
|
<p className={"text-sm my-1 whitespace-pre-wrap"}>{file.note}</p>
|
||||||
|
<p className={"items-center mt-1"}>
|
||||||
|
<a
|
||||||
|
href={"https://www.moyu.moe/user/" + file.user.id}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
className={
|
||||||
|
"badge-soft badge-primary text-xs mr-2 hover:shadow-xs transition-shadow"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={file.user.avatar}
|
||||||
|
className={"w-4 h-4 rounded-full"}
|
||||||
|
alt={"avatar"}
|
||||||
|
/>
|
||||||
|
{file.user.name}
|
||||||
|
</Badge>
|
||||||
|
</a>
|
||||||
|
<Badge className={"badge-soft badge-secondary text-xs mr-2"}>
|
||||||
|
<MdOutlineArchive size={16} className={"inline-block"} />
|
||||||
|
{file.size}
|
||||||
|
</Badge>
|
||||||
|
{tags.map((p) => (
|
||||||
|
<Badge className={"badge-soft badge-info text-xs mr-2"} key={p}>
|
||||||
|
{p}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className={"flex flex-row items-center"}>
|
||||||
|
<a
|
||||||
|
href={`https://www.moyu.moe/patch/${patchID}/resource#kun_patch_resource_${file.id}`}
|
||||||
|
target="_blank"
|
||||||
|
className={"btn btn-primary btn-soft btn-square"}
|
||||||
|
>
|
||||||
|
<MdOutlineOpenInNew size={24} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@@ -65,7 +65,9 @@ export default function TagsPage() {
|
|||||||
navigate(`/tag/${tag.name}`);
|
navigate(`/tag/${tag.name}`);
|
||||||
}}
|
}}
|
||||||
key={tag.name}
|
key={tag.name}
|
||||||
className={"m-1 cursor-pointer badge-soft badge-primary shadow-xs"}
|
className={
|
||||||
|
"m-1 cursor-pointer badge-soft badge-primary shadow-xs"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{tag.name +
|
{tag.name +
|
||||||
(tag.resources_count > 0 ? ` (${tag.resources_count})` : "")}
|
(tag.resources_count > 0 ? ` (${tag.resources_count})` : "")}
|
||||||
|
@@ -12,6 +12,10 @@ export default defineConfig({
|
|||||||
// target: "https://res.nyne.dev",
|
// target: "https://res.nyne.dev",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
|
"https://www.moyu.moe": {
|
||||||
|
target: "https://www.moyu.moe",
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user