mirror of
https://github.com/wgh136/nysoure.git
synced 2025-12-16 07:51:14 +00:00
Compare commits
3 Commits
8e2ab62297
...
31b9fb5d45
| Author | SHA1 | Date | |
|---|---|---|---|
| 31b9fb5d45 | |||
| 116efcdf93 | |||
| 9ad8d9d7e9 |
@@ -156,8 +156,8 @@ export const i18nData = {
|
||||
"Cloudflare Turnstile Secret Key": "Cloudflare Turnstile 密钥",
|
||||
"If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.":
|
||||
"如果设置了 Cloudflare Turnstile 密钥,将在注册和下载时启用验证",
|
||||
"The first image will be used as the cover image":
|
||||
"第一张图片将用作封面图片",
|
||||
"You can select a cover image using the radio button in the Cover column":
|
||||
"您可以使用封面列中的单选按钮选择封面图片",
|
||||
"Please enter a search keyword": "请输入搜索关键词",
|
||||
"Searching...": "搜索中...",
|
||||
"Create Tag": "创建标签",
|
||||
@@ -425,8 +425,8 @@ export const i18nData = {
|
||||
"Cloudflare Turnstile Secret Key": "Cloudflare Turnstile 密鑰",
|
||||
"If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.":
|
||||
"如果設置了 Cloudflare Turnstile 密鑰,將在註冊和下載時啟用驗證",
|
||||
"The first image will be used as the cover image":
|
||||
"第一張圖片將用作封面圖片",
|
||||
"You can select a cover image using the radio button in the Cover column":
|
||||
"您可以使用封面列中的單選按鈕選擇封面圖片",
|
||||
"Please enter a search keyword": "請輸入搜尋關鍵字",
|
||||
"Searching...": "搜尋中...",
|
||||
"Create Tag": "創建標籤",
|
||||
|
||||
@@ -48,6 +48,7 @@ export interface CreateResourceParams {
|
||||
tags: number[];
|
||||
article: string;
|
||||
images: number[];
|
||||
cover_id?: number;
|
||||
gallery: number[];
|
||||
gallery_nsfw: number[];
|
||||
characters: CharacterParams[];
|
||||
@@ -94,6 +95,7 @@ export interface ResourceDetails {
|
||||
releaseDate?: string;
|
||||
tags: Tag[];
|
||||
images: Image[];
|
||||
coverId?: number;
|
||||
files: RFile[];
|
||||
author: User;
|
||||
views: number;
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function EditResourcePage() {
|
||||
const [tags, setTags] = useState<Tag[]>([]);
|
||||
const [article, setArticle] = useState<string>("");
|
||||
const [images, setImages] = useState<number[]>([]);
|
||||
const [coverId, setCoverId] = useState<number | undefined>(undefined);
|
||||
const [links, setLinks] = useState<{ label: string; url: string }[]>([]);
|
||||
const [galleryImages, setGalleryImages] = useState<number[]>([]);
|
||||
const [galleryNsfw, setGalleryNsfw] = useState<number[]>([]);
|
||||
@@ -59,6 +60,7 @@ export default function EditResourcePage() {
|
||||
setTags(data.tags);
|
||||
setArticle(data.article);
|
||||
setImages(data.images.map((i) => i.id));
|
||||
setCoverId(data.coverId);
|
||||
setLinks(data.links ?? []);
|
||||
setGalleryImages(data.gallery ?? []);
|
||||
setGalleryNsfw(data.galleryNsfw ?? []);
|
||||
@@ -106,6 +108,7 @@ export default function EditResourcePage() {
|
||||
tags: tags.map((tag) => tag.id),
|
||||
article: article,
|
||||
images: images,
|
||||
cover_id: coverId,
|
||||
links: links,
|
||||
gallery: galleryImages,
|
||||
gallery_nsfw: galleryNsfw,
|
||||
@@ -328,7 +331,7 @@ export default function EditResourcePage() {
|
||||
"Images will not be displayed automatically, you need to reference them in the description",
|
||||
)}
|
||||
</p>
|
||||
<p>{t("The first image will be used as the cover image")}</p>
|
||||
<p>{t("You can select a cover image using the radio button in the Cover column")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -339,6 +342,7 @@ export default function EditResourcePage() {
|
||||
<tr>
|
||||
<td>{t("Preview")}</td>
|
||||
<td>{"Markdown"}</td>
|
||||
<td>{t("Cover")}</td>
|
||||
<td>{t("Gallery")}</td>
|
||||
<td>{"Nsfw"}</td>
|
||||
<td>{t("Action")}</td>
|
||||
@@ -368,6 +372,15 @@ export default function EditResourcePage() {
|
||||
<MdContentCopy />
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="radio"
|
||||
name="cover"
|
||||
className="radio radio-accent"
|
||||
checked={coverId === image}
|
||||
onChange={() => setCoverId(image)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -409,6 +422,9 @@ export default function EditResourcePage() {
|
||||
const newImages = [...images];
|
||||
newImages.splice(index, 1);
|
||||
setImages(newImages);
|
||||
if (coverId === id) {
|
||||
setCoverId(undefined);
|
||||
}
|
||||
network.deleteImage(id);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { app } from "../app.ts";
|
||||
import { Resource, RSort, Statistics } from "../network/models.ts";
|
||||
import { useTranslation } from "../utils/i18n";
|
||||
import { useAppContext } from "../components/AppContext.tsx";
|
||||
import Select from "../components/select.tsx";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useNavigator } from "../components/navigator.tsx";
|
||||
import {
|
||||
@@ -40,8 +39,18 @@ export default function HomePage() {
|
||||
<>
|
||||
<HomeHeader />
|
||||
<div className={"flex pt-4 px-4 items-center"}>
|
||||
<Select
|
||||
values={[
|
||||
<select
|
||||
value={order}
|
||||
className="select select-primary max-w-72"
|
||||
onChange={(e) => {
|
||||
const order = parseInt(e.target.value);
|
||||
setOrder(order);
|
||||
if (appContext) {
|
||||
appContext.set("home_page_order", order);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{[
|
||||
t("Time Ascending"),
|
||||
t("Time Descending"),
|
||||
t("Views Ascending"),
|
||||
@@ -50,15 +59,12 @@ export default function HomePage() {
|
||||
t("Downloads Descending"),
|
||||
t("Release Date Ascending"),
|
||||
t("Release Date Descending"),
|
||||
]}
|
||||
current={order}
|
||||
onSelected={(index) => {
|
||||
setOrder(index);
|
||||
if (appContext) {
|
||||
appContext.set("home_page_order", index);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
].map((label, idx) => (
|
||||
<option key={idx} value={idx}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<ResourcesView
|
||||
key={`home_page_${order}`}
|
||||
|
||||
@@ -28,6 +28,7 @@ export default function PublishPage() {
|
||||
const [tags, setTags] = useState<Tag[]>([]);
|
||||
const [article, setArticle] = useState<string>("");
|
||||
const [images, setImages] = useState<number[]>([]);
|
||||
const [coverId, setCoverId] = useState<number | undefined>(undefined);
|
||||
const [links, setLinks] = useState<{ label: string; url: string }[]>([]);
|
||||
const [galleryImages, setGalleryImages] = useState<number[]>([]);
|
||||
const [galleryNsfw, setGalleryNsfw] = useState<number[]>([]);
|
||||
@@ -48,6 +49,7 @@ export default function PublishPage() {
|
||||
setTags(data.tags || []);
|
||||
setArticle(data.article || "");
|
||||
setImages(data.images || []);
|
||||
setCoverId(data.cover_id || undefined);
|
||||
setLinks(data.links || []);
|
||||
setGalleryImages(data.gallery || []);
|
||||
setGalleryNsfw(data.gallery_nsfw || []);
|
||||
@@ -64,6 +66,7 @@ export default function PublishPage() {
|
||||
tags: tags,
|
||||
article: article,
|
||||
images: images,
|
||||
cover_id: coverId,
|
||||
links: links,
|
||||
gallery: galleryImages,
|
||||
gallery_nsfw: galleryNsfw,
|
||||
@@ -73,7 +76,7 @@ export default function PublishPage() {
|
||||
const dataString = JSON.stringify(data);
|
||||
localStorage.setItem("publish_data", dataString);
|
||||
}
|
||||
}, [altTitles, article, images, tags, title, links, galleryImages, galleryNsfw, characters, releaseDate]);
|
||||
}, [altTitles, article, images, coverId, tags, title, links, galleryImages, galleryNsfw, characters, releaseDate]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
@@ -120,6 +123,7 @@ export default function PublishPage() {
|
||||
tags: tags.map((tag) => tag.id),
|
||||
article: article,
|
||||
images: images,
|
||||
cover_id: coverId,
|
||||
links: links,
|
||||
gallery: galleryImages,
|
||||
gallery_nsfw: galleryNsfw,
|
||||
@@ -344,7 +348,7 @@ export default function PublishPage() {
|
||||
"Images will not be displayed automatically, you need to reference them in the description",
|
||||
)}
|
||||
</p>
|
||||
<p>{t("The first image will be used as the cover image")}</p>
|
||||
<p>{t("You can select a cover image using the radio button in the Cover column")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -355,7 +359,8 @@ export default function PublishPage() {
|
||||
<tr>
|
||||
<td>{t("Preview")}</td>
|
||||
<td>{"Markdown"}</td>
|
||||
<td>{"Gallery"}</td>
|
||||
<td>{t("Cover")}</td>
|
||||
<td>{t("Gallery")}</td>
|
||||
<td>{"Nsfw"}</td>
|
||||
<td>{t("Action")}</td>
|
||||
</tr>
|
||||
@@ -384,6 +389,15 @@ export default function PublishPage() {
|
||||
<MdContentCopy />
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="radio"
|
||||
name="cover"
|
||||
className="radio radio-accent"
|
||||
checked={coverId === image}
|
||||
onChange={() => setCoverId(image)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -425,6 +439,9 @@ export default function PublishPage() {
|
||||
const newImages = [...images];
|
||||
newImages.splice(index, 1);
|
||||
setImages(newImages);
|
||||
if (coverId === id) {
|
||||
setCoverId(undefined);
|
||||
}
|
||||
network.deleteImage(id);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -14,6 +14,7 @@ type Resource struct {
|
||||
ReleaseDate *time.Time
|
||||
Article string
|
||||
Images []Image `gorm:"many2many:resource_images;"`
|
||||
CoverID *uint
|
||||
Tags []Tag `gorm:"many2many:resource_tags;"`
|
||||
Files []File `gorm:"foreignKey:ResourceID"`
|
||||
UserID uint
|
||||
@@ -52,6 +53,7 @@ type ResourceDetailView struct {
|
||||
ReleaseDate *time.Time `json:"releaseDate,omitempty"`
|
||||
Tags []TagView `json:"tags"`
|
||||
Images []ImageView `json:"images"`
|
||||
CoverID *uint `json:"coverId,omitempty"`
|
||||
Files []FileView `json:"files"`
|
||||
Author UserView `json:"author"`
|
||||
Views uint `json:"views"`
|
||||
@@ -78,7 +80,18 @@ func (r *Resource) ToView() ResourceView {
|
||||
}
|
||||
|
||||
var image *ImageView
|
||||
if len(r.Images) > 0 {
|
||||
if r.CoverID != nil {
|
||||
// Use the cover image if specified
|
||||
for _, img := range r.Images {
|
||||
if img.ID == *r.CoverID {
|
||||
v := img.ToView()
|
||||
image = &v
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no cover is set or cover image not found, use the first image
|
||||
if image == nil && len(r.Images) > 0 {
|
||||
v := r.Images[0].ToView()
|
||||
image = &v
|
||||
}
|
||||
@@ -122,6 +135,7 @@ func (r *Resource) ToDetailView() ResourceDetailView {
|
||||
ReleaseDate: r.ReleaseDate,
|
||||
Tags: tags,
|
||||
Images: images,
|
||||
CoverID: r.CoverID,
|
||||
Files: files,
|
||||
Author: r.User.ToView(),
|
||||
Views: r.Views,
|
||||
|
||||
@@ -34,6 +34,7 @@ type ResourceParams struct {
|
||||
Tags []uint `json:"tags"`
|
||||
Article string `json:"article"`
|
||||
Images []uint `json:"images"`
|
||||
CoverID *uint `json:"cover_id"`
|
||||
Gallery []uint `json:"gallery"`
|
||||
GalleryNsfw []uint `json:"gallery_nsfw"`
|
||||
Characters []CharacterParams `json:"characters"`
|
||||
@@ -110,6 +111,14 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) {
|
||||
}
|
||||
date = &parsedDate
|
||||
}
|
||||
// Validate CoverID if provided
|
||||
var coverID *uint
|
||||
if params.CoverID != nil && *params.CoverID != 0 {
|
||||
if !slices.Contains(params.Images, *params.CoverID) {
|
||||
return 0, model.NewRequestError("Cover ID must be one of the resource images")
|
||||
}
|
||||
coverID = params.CoverID
|
||||
}
|
||||
r := model.Resource{
|
||||
Title: params.Title,
|
||||
AlternativeTitles: params.AlternativeTitles,
|
||||
@@ -117,6 +126,7 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) {
|
||||
Links: params.Links,
|
||||
ReleaseDate: date,
|
||||
Images: images,
|
||||
CoverID: coverID,
|
||||
Tags: tags,
|
||||
UserID: uid,
|
||||
Gallery: gallery,
|
||||
@@ -548,11 +558,21 @@ func UpdateResource(uid, rid uint, params *ResourceParams) error {
|
||||
date = &parsedDate
|
||||
}
|
||||
|
||||
// Validate CoverID if provided
|
||||
var coverID *uint
|
||||
if params.CoverID != nil && *params.CoverID != 0 {
|
||||
if !slices.Contains(params.Images, *params.CoverID) {
|
||||
return model.NewRequestError("Cover ID must be one of the resource images")
|
||||
}
|
||||
coverID = params.CoverID
|
||||
}
|
||||
|
||||
r.Title = params.Title
|
||||
r.AlternativeTitles = params.AlternativeTitles
|
||||
r.Article = params.Article
|
||||
r.Links = params.Links
|
||||
r.ReleaseDate = date
|
||||
r.CoverID = coverID
|
||||
r.Gallery = gallery
|
||||
r.GalleryNsfw = nsfw
|
||||
r.Characters = characters
|
||||
|
||||
Reference in New Issue
Block a user