Show file uploader.

This commit is contained in:
2025-07-05 20:40:48 +08:00
parent 2dfa2e3e02
commit a169918b93
5 changed files with 49 additions and 22 deletions

View File

@@ -1,3 +1,4 @@
{ {
"quoteProps": "consistent" "quoteProps": "consistent",
"endOfLine": "auto"
} }

View File

@@ -100,7 +100,7 @@ export interface RFile {
description: string; description: string;
size: number; size: number;
is_redirect: boolean; is_redirect: boolean;
user_id: number; user: User;
} }
export interface UploadingFile { export interface UploadingFile {

View File

@@ -23,7 +23,7 @@ import Markdown from "react-markdown";
import "../markdown.css"; import "../markdown.css";
import Loading from "../components/loading.tsx"; import Loading from "../components/loading.tsx";
import { import {
MdAdd, MdAdd, MdOutlineArchive,
MdOutlineArticle, MdOutlineArticle,
MdOutlineComment, MdOutlineComment,
MdOutlineDataset, MdOutlineDataset,
@@ -41,7 +41,7 @@ import Pagination from "../components/pagination.tsx";
import showPopup, { useClosePopup } from "../components/popup.tsx"; import showPopup, { useClosePopup } from "../components/popup.tsx";
import { Turnstile } from "@marsidev/react-turnstile"; import { Turnstile } from "@marsidev/react-turnstile";
import Button from "../components/button.tsx"; import Button from "../components/button.tsx";
import Badge, { BadgeAccent } from "../components/badge.tsx"; import Badge from "../components/badge.tsx";
import Input, { TextArea } from "../components/input.tsx"; import Input, { TextArea } from "../components/input.tsx";
import { useAppContext } from "../components/AppContext.tsx"; import { useAppContext } from "../components/AppContext.tsx";
import { BiLogoSteam } from "react-icons/bi"; import { BiLogoSteam } from "react-icons/bi";
@@ -609,6 +609,10 @@ function FileTile({ file }: { file: RFile }) {
const { t } = useTranslation(); const { t } = useTranslation();
const userLink = `/user/${encodeURIComponent(file.user.username)}`;
const navigate = useNavigate();
return ( return (
<div className={"card shadow bg-base-100 mb-4"}> <div className={"card shadow bg-base-100 mb-4"}>
<div className={"p-4 flex flex-row items-center"}> <div className={"p-4 flex flex-row items-center"}>
@@ -617,11 +621,34 @@ function FileTile({ file }: { file: RFile }) {
<p className={"text-sm my-1 whitespace-pre-wrap"}> <p className={"text-sm my-1 whitespace-pre-wrap"}>
{file.description} {file.description}
</p> </p>
<p> <div className={"flex items-center mt-1"}>
<BadgeAccent className={"mt-1"}> <a
href={userLink}
onClick={(e) => {
e.preventDefault();
navigate(userLink);
}}
>
<Badge
className={
"badge-soft badge-primary text-xs mr-2 hover:shadow-xs transition-shadow"
}
>
<img
src={network.getUserAvatar(file.user)}
className={"w-4 h-4 rounded-full"}
alt={"avatar"}
/>
{file.user.username}
</Badge>
</a>
<Badge className={"badge-soft badge-secondary text-xs mr-2"}>
<MdOutlineArchive size={16} className={"inline-block"} />
{file.is_redirect ? t("Redirect") : fileSizeToString(file.size)} {file.is_redirect ? t("Redirect") : fileSizeToString(file.size)}
</BadgeAccent> </Badge>
</p> <DeleteFileDialog fileId={file.id} uploaderId={file.user.id} />
<UpdateFileInfoDialog file={file} />
</div>
</div> </div>
<div className={"flex flex-row items-center"}> <div className={"flex flex-row items-center"}>
<button <button
@@ -638,8 +665,6 @@ function FileTile({ file }: { file: RFile }) {
> >
<MdOutlineDownload size={24} /> <MdOutlineDownload size={24} />
</button> </button>
<DeleteFileDialog fileId={file.id} uploaderId={file.user_id} />
<UpdateFileInfoDialog file={file} />
</div> </div>
</div> </div>
</div> </div>
@@ -1116,14 +1141,14 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) {
setLoading(false); setLoading(false);
}; };
if (!app.isAdmin() && app.user?.id !== file.user_id) { if (!app.isAdmin() && app.user?.id !== file.user.id) {
return <></>; return <></>;
} }
return ( return (
<> <>
<button <button
className={"btn btn-primary btn-ghost btn-circle ml-1"} className={"btn btn-primary btn-ghost btn-circle btn-sm ml-1"}
onClick={() => { onClick={() => {
const dialog = document.getElementById( const dialog = document.getElementById(
`update_file_info_dialog_${file.id}`, `update_file_info_dialog_${file.id}`,
@@ -1131,7 +1156,7 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) {
dialog.showModal(); dialog.showModal();
}} }}
> >
<MdOutlineEdit size={20} className={"inline-block"} /> <MdOutlineEdit size={16} className={"inline-block"} />
</button> </button>
<dialog id={`update_file_info_dialog_${file.id}`} className="modal"> <dialog id={`update_file_info_dialog_${file.id}`} className="modal">
<div className="modal-box"> <div className="modal-box">
@@ -1275,13 +1300,13 @@ function DeleteFileDialog({
return ( return (
<> <>
<button <button
className={"btn btn-error btn-ghost btn-circle ml-1"} className={"btn btn-error btn-ghost btn-circle btn-sm ml-1"}
onClick={() => { onClick={() => {
const dialog = document.getElementById(id) as HTMLDialogElement; const dialog = document.getElementById(id) as HTMLDialogElement;
dialog.showModal(); dialog.showModal();
}} }}
> >
<MdOutlineDelete size={20} className={"inline-block"} /> <MdOutlineDelete size={16} className={"inline-block"} />
</button> </button>
<dialog id={id} className="modal"> <dialog id={id} className="modal">
<div className="modal-box"> <div className="modal-box">

View File

@@ -38,6 +38,7 @@ func GetResourceByID(id uint) (model.Resource, error) {
Preload("Images"). Preload("Images").
Preload("Tags"). Preload("Tags").
Preload("Files"). Preload("Files").
Preload("Files.User").
First(&r, id).Error; err != nil { First(&r, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return model.Resource{}, model.NewNotFoundError("Resource not found") return model.Resource{}, model.NewNotFoundError("Resource not found")

View File

@@ -26,7 +26,7 @@ type FileView struct {
Description string `json:"description"` Description string `json:"description"`
Size int64 `json:"size"` Size int64 `json:"size"`
IsRedirect bool `json:"is_redirect"` IsRedirect bool `json:"is_redirect"`
UserID uint `json:"user_id"` User UserView `json:"user"`
} }
func (f *File) ToView() *FileView { func (f *File) ToView() *FileView {
@@ -36,6 +36,6 @@ func (f *File) ToView() *FileView {
Description: f.Description, Description: f.Description,
Size: f.Size, Size: f.Size,
IsRedirect: f.RedirectUrl != "", IsRedirect: f.RedirectUrl != "",
UserID: f.UserID, User: f.User.ToView(),
} }
} }