Add effect because I coldn't resist
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
import { Attachment, denormalizeSystemInformation, type Piece, pieceCache, User } from "@/cache";
|
||||
import { client } from "@/client";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { useLoading } from "@/hooks/useLoading";
|
||||
import { Updater } from "@/hooks/useStore";
|
||||
import { useLoadingEffect } from "@/hooks/useLoading";
|
||||
import { mapProp, Update, Updater } from "@/hooks/useStore";
|
||||
import { timeout } from "@/lib/utils";
|
||||
import { Label } from "@radix-ui/react-label";
|
||||
import type { Attachment, Piece } from "backend/database";
|
||||
import clsx from "clsx";
|
||||
import { PieceId } from "common";
|
||||
import { getMediaTypeForFilename } from "common/MediaType";
|
||||
import { ELYSIA_FORM_DATA } from "elysia";
|
||||
import { Cause, Effect, Option } from "effect";
|
||||
import { constant } from "effect/Function";
|
||||
import { Download, Loader2, Trash, UploadCloud } from "lucide-react";
|
||||
import { DragEventHandler, FormEventHandler, MouseEvent, useCallback, useId, useState } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
@@ -19,10 +20,14 @@ export function Piece() {
|
||||
|
||||
const id = PieceId(useParams().pieceId!);
|
||||
|
||||
const piece = useLoading(() => client.piece.get({ query: { id } }));
|
||||
const attachments = useLoading(() => client.piece({ pieceId: id }).attachment.get());
|
||||
const { isLoading, error, data, setData } = useLoadingEffect(Effect.uninterruptible(pieceCache.get(id)));
|
||||
|
||||
if (piece.isLoading || attachments.isLoading) {
|
||||
const setAttachments = useCallback((action: Update<readonly Attachment[]>) => {
|
||||
setData!(mapProp("attachments", action));
|
||||
Effect.runFork(pieceCache.invalidate(id));
|
||||
}, [setData]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="w-full h-full overflow-hidden flex items-center justify-center">
|
||||
<div>Ładowanie…</div>
|
||||
@@ -32,18 +37,16 @@ export function Piece() {
|
||||
|
||||
return (
|
||||
<div className="p-4 overflow-y-auto flex flex-wrap items-start gap-4">
|
||||
{piece.error !== null || attachments.error !== null ? (
|
||||
`Wystąpił błąd: ${[piece.error?.value, attachments.error?.value].filter(Boolean).join(", ")}`
|
||||
) : piece.data[0] === undefined ? (
|
||||
"Utwór nie istnieje"
|
||||
{error !== null ? (
|
||||
Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${error.value}`
|
||||
) : (<>
|
||||
<div className="flex flex-col gap-4 p-4 border rounded">
|
||||
<h3 className="font-bold text-lg">Utwór</h3>
|
||||
<PieceForm piece={piece.data[0]} />
|
||||
<PieceForm piece={data} />
|
||||
<h3 className="font-bold text-lg">Załączniki</h3>
|
||||
<AttachmentForm pieceId={piece.data[0].pieceId} setAttachments={attachments.setData} />
|
||||
<AttachmentForm pieceId={id} setAttachments={setAttachments} />
|
||||
</div>
|
||||
<Attachments pieceId={piece.data[0].pieceId} attachments={attachments.data} setAttachments={attachments.setData} />
|
||||
<Attachments pieceId={id} attachments={data.attachments} setAttachments={setAttachments} />
|
||||
</>)}
|
||||
</div>
|
||||
);
|
||||
@@ -58,9 +61,9 @@ namespace PieceForm {
|
||||
function PieceForm(props: PieceForm.Props) {
|
||||
|
||||
const [name, setName] = useState(props.piece.name);
|
||||
const [composer, setComposer] = useState(props.piece.composer ?? "");
|
||||
const [lyricist, setLyricist] = useState(props.piece.lyricist ?? "");
|
||||
const [arranger, setArranger] = useState(props.piece.arranger ?? "");
|
||||
const [composer, setComposer] = useState(() => Option.getOrElse(props.piece.composer, constant("")));
|
||||
const [lyricist, setLyricist] = useState(() => Option.getOrElse(props.piece.lyricist, constant("")));
|
||||
const [arranger, setArranger] = useState(() => Option.getOrElse(props.piece.arranger, constant("")));
|
||||
|
||||
const nameId = useId();
|
||||
const composerId = useId();
|
||||
@@ -83,7 +86,7 @@ function PieceForm(props: PieceForm.Props) {
|
||||
arranger: arranger.length > 0 ? arranger : null,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
if (error !== null) {
|
||||
console.error(error.value);
|
||||
return;
|
||||
}
|
||||
@@ -144,7 +147,7 @@ namespace Attachments {
|
||||
export interface Props {
|
||||
readonly pieceId: PieceId;
|
||||
readonly attachments: readonly Attachment[];
|
||||
readonly setAttachments: Updater<Attachment[]>;
|
||||
readonly setAttachments: Updater<readonly Attachment[]>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,28 +181,27 @@ function Attachments(props: Attachments.Props) {
|
||||
namespace AttachmentRow {
|
||||
export interface Props {
|
||||
readonly attachment: Attachment;
|
||||
readonly setAttachments: Updater<Attachment[]>;
|
||||
readonly setAttachments: Updater<readonly Attachment[]>;
|
||||
}
|
||||
}
|
||||
|
||||
function AttachmentRow(props: AttachmentRow.Props) {
|
||||
|
||||
const download = useCallback(async () => {
|
||||
const { error, data: _data } = await client
|
||||
const { error, data } = await client
|
||||
.piece({ pieceId: props.attachment.pieceId })
|
||||
.attachment({ attachmentId: props.attachment.attachmentId })
|
||||
.get();
|
||||
.get()
|
||||
|
||||
if (error !== null) {
|
||||
console.error(error.value);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = _data as unknown as typeof _data[ELYSIA_FORM_DATA];
|
||||
const url = URL.createObjectURL(data.data);
|
||||
const url = URL.createObjectURL(data);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = data.filename;
|
||||
a.download = data.name;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}, [props.attachment.attachmentId, props.attachment.pieceId]);
|
||||
@@ -211,7 +213,7 @@ function AttachmentRow(props: AttachmentRow.Props) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const { error, data: _data } = await client
|
||||
const { error, data } = await client
|
||||
.piece({ pieceId: props.attachment.pieceId })
|
||||
.attachment({ attachmentId: props.attachment.attachmentId })
|
||||
.get();
|
||||
@@ -221,8 +223,7 @@ function AttachmentRow(props: AttachmentRow.Props) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = _data as unknown as typeof _data[ELYSIA_FORM_DATA];
|
||||
const url = URL.createObjectURL(data.data);
|
||||
const url = URL.createObjectURL(data);
|
||||
window.open(url, "_target");
|
||||
URL.revokeObjectURL(url);
|
||||
}, [props.attachment.mediaType, props.attachment.attachmentId, props.attachment.pieceId]);
|
||||
@@ -256,16 +257,18 @@ function AttachmentRow(props: AttachmentRow.Props) {
|
||||
props.attachment.filename
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>{props.attachment.mediaType}</TableCell>
|
||||
<TableCell className="text-center font-mono text-xs">
|
||||
{props.attachment.createdAt}
|
||||
{props.attachment.createdBy !== null && <><br />przez {props.attachment.createdBy}</>}
|
||||
<TableCell>
|
||||
{props.attachment.mediaType}
|
||||
</TableCell>
|
||||
<TableCell className="text-center font-mono text-xs">
|
||||
{props.attachment.modifiedAt === null && props.attachment.modifiedBy === null ? "\u2014"
|
||||
: props.attachment.modifiedAt !== null && props.attachment.modifiedBy === null ? props.attachment.modifiedAt
|
||||
: props.attachment.modifiedAt === null ? `przez ${props.attachment.createdBy}`
|
||||
: <>{props.attachment.createdAt}<br />przez {props.attachment.createdBy}</>}
|
||||
<TableCell className="text-center text-xs">
|
||||
{props.attachment.createdAt}
|
||||
{Option.isSome(props.attachment.createdBy) && <><br />przez {props.attachment.createdBy.value.username}</>}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs">
|
||||
{Option.isNone(props.attachment.modifiedAt) && Option.isNone(props.attachment.modifiedBy) ? "\u2014"
|
||||
: Option.isSome(props.attachment.modifiedAt) && Option.isNone(props.attachment.modifiedBy) ? props.attachment.modifiedAt.value
|
||||
: Option.isNone(props.attachment.modifiedAt) ? `przez ${(props.attachment.modifiedBy as Option.Some<User>).value.username}`
|
||||
: <>{props.attachment.modifiedAt.value}<br />przez {(props.attachment.modifiedBy as Option.Some<User>).value.username}</>}
|
||||
</TableCell>
|
||||
<TableCell className="text-center flex justify-center gap-4">
|
||||
<Button type="button" variant="ghost" size="icon" title="Pobierz" onClick={download}>
|
||||
@@ -282,7 +285,7 @@ function AttachmentRow(props: AttachmentRow.Props) {
|
||||
namespace AttachmentForm {
|
||||
export interface Props {
|
||||
readonly pieceId: PieceId;
|
||||
readonly setAttachments: Updater<Attachment[]>;
|
||||
readonly setAttachments: Updater<readonly Attachment[]>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,8 +326,10 @@ function AttachmentForm(props: AttachmentForm.Props) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const attachment = await Effect.runPromise(denormalizeSystemInformation(data));
|
||||
|
||||
props.setAttachments((prev) => {
|
||||
const next = [...prev, data];
|
||||
const next = [...prev, attachment];
|
||||
next.sort((a, b) => a.filename.localeCompare(b.filename));
|
||||
return next;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user