Pull title, description and tags from an F95zone thread
Admin/uploader pastes a thread link and presses "Подтянуть" on the new game and edit forms; the backend reads the public first post (no login). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ac164d1782
commit
ae8e175376
5 files changed
+227
No files matched your search
@@ -4,6 +4,7 @@ import { api, formatSize, uploadRaw, type GameDetail, type Version } from "../ap
|
||||
import { useAuth } from "../auth";
|
||||
import { getRecent, pushRecent } from "../recent";
|
||||
import { Cover, ErrorNote, Spinner } from "../ui/Bits";
|
||||
import { F95Fetch } from "../ui/F95Fetch";
|
||||
import { Icon } from "../ui/Icon";
|
||||
import { useUi } from "../ui/Ui";
|
||||
|
||||
@@ -358,6 +359,11 @@ function GameEditor({ game, onDone }: { game: GameDetail; onDone: (changed: bool
|
||||
};
|
||||
return (
|
||||
<form className="editor" onSubmit={(e) => { e.preventDefault(); void save(); }}>
|
||||
<F95Fetch onLoaded={(m) => {
|
||||
setTitle(m.title);
|
||||
setDescription(m.description);
|
||||
setTags(m.tags.join(", "));
|
||||
}} />
|
||||
<label className="field"><span>Название</span><input value={title} onChange={(e) => setTitle(e.target.value)} /></label>
|
||||
<label className="field"><span>Описание</span><textarea value={description} onChange={(e) => setDescription(e.target.value)} rows={5} /></label>
|
||||
<label className="field"><span>Теги</span><input value={tags} onChange={(e) => setTags(e.target.value)} placeholder="через запятую: романтика, драма" /></label>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, type DragEvent, type FormEvent } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { api, formatSize, uploadRaw, type Game } from "../api";
|
||||
import { ErrorNote, PageHeader } from "../ui/Bits";
|
||||
import { F95Fetch } from "../ui/F95Fetch";
|
||||
import { Icon } from "../ui/Icon";
|
||||
|
||||
const ARCHIVE = /\.(zip|7z|rar|tar|gz|tgz|bz2|xz)$/i;
|
||||
@@ -80,6 +81,11 @@ export function NewGame() {
|
||||
)}
|
||||
</label>
|
||||
|
||||
<F95Fetch onLoaded={(m) => {
|
||||
setTitle(m.title);
|
||||
setDescription(m.description);
|
||||
setTags(m.tags.join(", "));
|
||||
}} />
|
||||
<div className="form-row">
|
||||
<label className="field">
|
||||
<span>Название</span>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useState } from "react";
|
||||
import { api } from "../api";
|
||||
|
||||
export interface F95Meta {
|
||||
url: string;
|
||||
title: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
/** Link to an F95zone thread + a button that pulls the title, description and tags from it. */
|
||||
export function F95Fetch({ onLoaded }: { onLoaded: (m: F95Meta) => void }) {
|
||||
const [url, setUrl] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const load = async () => {
|
||||
setBusy(true);
|
||||
setError("");
|
||||
try {
|
||||
onLoaded(await api.post<F95Meta>("/api/metadata/f95", { url }));
|
||||
} catch (e) {
|
||||
setError((e as Error).message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="field">
|
||||
<span>Ссылка на тему F95zone</span>
|
||||
<div style={{ display: "flex", gap: "0.5rem" }}>
|
||||
<input value={url} onChange={(e) => setUrl(e.target.value)} style={{ flex: 1 }}
|
||||
placeholder="https://f95zone.to/threads/…"
|
||||
onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); if (url.trim() && !busy) void load(); } }} />
|
||||
<button type="button" className="btn" disabled={!url.trim() || busy} onClick={() => void load()}>
|
||||
{busy ? "Загрузка…" : "Подтянуть"}
|
||||
</button>
|
||||
</div>
|
||||
<span className="hint">Заполнит название, описание и теги. Результат можно поправить перед сохранением.</span>
|
||||
{error && <span className="hint" style={{ color: "var(--danger, #e5484d)" }}>{error}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user