Files
ForbiddenStarsApp/frontend/src/components/MatchMedia.tsx
T

109 lines
3.0 KiB
TypeScript

import { Paperclip } from "lucide-react";
import { useRef } from "react";
import { ApiError } from "../api/client";
import { useToast } from "../context/ToastContext";
import type { AttachmentRead } from "../domain/types";
/**
* Сетка фото партии. Переиспользуется на странице партии (игрок) и в админ-правке —
* URL/хуки приходят снаружи (cookie игрока и админа доходят до своих путей).
*/
export function MatchMedia({
attachments,
editable = false,
onUpload,
onDelete,
pending = false,
max = 10,
}: {
attachments: AttachmentRead[];
editable?: boolean;
onUpload?: (file: File) => Promise<unknown>;
onDelete?: (id: number) => Promise<unknown>;
pending?: boolean;
max?: number;
}) {
const toast = useToast();
const fileRef = useRef<HTMLInputElement>(null);
const full = attachments.length >= max;
const pick = async (e: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(e.target.files ?? []);
e.target.value = "";
if (!onUpload) return;
let slots = max - attachments.length;
for (const f of files) {
if (slots <= 0) {
toast.show(`Максимум ${max} фото`);
break;
}
try {
await onUpload(f);
slots--;
} catch (err) {
toast.show(err instanceof ApiError ? err.message : "Не удалось загрузить");
break;
}
}
};
const remove = async (id: number) => {
if (!onDelete) return;
try {
await onDelete(id);
} catch (e) {
toast.show(e instanceof ApiError ? e.message : "Не удалось удалить");
}
};
if (!editable && attachments.length === 0) return null;
return (
<div>
{attachments.length > 0 && (
<div className="media-grid">
{attachments.map((a) => (
<div key={a.id} className="media-item">
<a href={a.url} target="_blank" rel="noreferrer">
<img src={a.url} alt="фото партии" />
</a>
{editable && (
<button
className="media-del"
onClick={() => remove(a.id)}
disabled={pending}
aria-label="Удалить фото"
>
✕
</button>
)}
</div>
))}
</div>
)}
{editable && !full && (
<>
<button
className="btn small"
style={{ marginTop: 8, gap: 6 }}
onClick={() => fileRef.current?.click()}
disabled={pending}
>
<Paperclip size={16} />
Прикрепить медиа
</button>
<input
ref={fileRef}
type="file"
accept="image/png,image/jpeg,image/webp"
multiple
style={{ display: "none" }}
onChange={pick}
/>
</>
)}
</div>
);
}