diff --git a/packages/frontend/src/routes/Attachment.tsx b/packages/frontend/src/routes/Attachment.tsx
index d010512..ac5e54c 100644
--- a/packages/frontend/src/routes/Attachment.tsx
+++ b/packages/frontend/src/routes/Attachment.tsx
@@ -4,7 +4,7 @@ import { AttachmentId } from "common";
import { Match } from "effect";
import JSZip from "jszip";
import { OpenSheetMusicDisplay } from "opensheetmusicdisplay";
-import { useCallback, useEffect, useRef } from "react";
+import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
export default function Attachment() {
@@ -14,19 +14,62 @@ export default function Attachment() {
const { isLoading, error, data } = useLoading(client.getAttachment(attachmentId), [attachmentId]);
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (error !== null) {
+ return (
+
+
+ Wystąpił błąd: {Match.value(error).pipe(
+ Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
+ Match.tag("NotFound", () => "Załącznik nie istnieje"),
+ Match.tag("Unauthenticated", () => "Zaloguj się, aby kontynuować"),
+ Match.tag("Unauthorized", () => "Nie posiadasz uprawnień"),
+ Match.exhaustive,
+ )}
+
+
+ );
+ }
+
+ if (data.mediaType === "application/vnd.recordare.musicxml" || data.mediaType === "application/vnd.recordare.musicxml+xml") {
+ return ;
+ } else if (data.mediaType === "application/pdf") {
+ return ;
+ } else {
+ return null;
+ }
+};
+
+namespace MusicXmlAttachment {
+ export interface Props {
+ readonly data: Uint8Array;
+ readonly mediaType:
+ | "application/vnd.recordare.musicxml"
+ | "application/vnd.recordare.musicxml+xml"
+ ;
+ }
+}
+
+function MusicXmlAttachment({ data, mediaType }: MusicXmlAttachment.Props) {
+
const containerRef = useRef(null);
const renderFn = useRef void)>(null);
const load = useCallback(async () => {
- if (isLoading || error !== null) return;
-
- let musixXmlData: Uint8Array = data.data;
+ let musixXmlData: Uint8Array = data;
/* If the file is the compressed .mxl file, we do the uncompression
* ourselves, because apparently OpenSheetMusicDisplay is incapable.
*/
- if (data.mediaType === "application/vnd.recordare.musicxml") {
+ if (mediaType === "application/vnd.recordare.musicxml") {
const zip = new JSZip();
await zip.loadAsync(musixXmlData);
@@ -78,7 +121,7 @@ export default function Attachment() {
render();
renderFn.current = render;
- }, [data, error, isLoading]);
+ }, [data]);
useEffect(() => void load(), [load]);
@@ -91,29 +134,39 @@ export default function Attachment() {
};
}, []);
- if (isLoading) {
- return (
-
- );
- }
-
- if (error !== null) {
- return (
-
-
- Wystąpił błąd: {Match.value(error).pipe(
- Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
- Match.tag("NotFound", () => "Załącznik nie istnieje"),
- Match.tag("Unauthenticated", () => "Zaloguj się, aby kontynuować"),
- Match.tag("Unauthorized", () => "Nie posiadasz uprawnień"),
- Match.exhaustive,
- )}
-
-
- );
- }
-
return ;
-};
+}
+
+namespace PdfAttachment {
+ export interface Props {
+ readonly data: Uint8Array;
+ readonly filename: string;
+ readonly mediaType: "application/pdf";
+ }
+}
+
+function PdfAttachment({
+ data,
+ filename,
+ mediaType,
+}: PdfAttachment.Props) {
+
+ const [url, setUrl] = useState("");
+
+ useLayoutEffect(() => {
+ const file = new File([data], filename, { type: mediaType });
+ const url = URL.createObjectURL(file);
+
+ setUrl(url);
+
+ return () => { URL.revokeObjectURL(url); };
+ }, [data, mediaType]);
+
+ return url && (
+
+ );
+}
diff --git a/packages/frontend/src/routes/Piece.tsx b/packages/frontend/src/routes/Piece.tsx
index 2b1d426..27d5697 100644
--- a/packages/frontend/src/routes/Piece.tsx
+++ b/packages/frontend/src/routes/Piece.tsx
@@ -233,21 +233,6 @@ function AttachmentRow(props: AttachmentRow.Props) {
URL.revokeObjectURL(url);
}).pipe(Effect.runPromise);
- const open = (event: MouseEvent) => Effect.gen(function* () {
- if (props.attachment.mediaType !== "application/pdf") {
- return;
- }
-
- event.preventDefault();
-
- const { data, mediaType } = yield* client.getAttachment(props.attachment.attachmentId);
- const blob = new Blob([data], { type: mediaType });
-
- const url = URL.createObjectURL(blob);
- window.open(url, "_target");
- URL.revokeObjectURL(url);
- }).pipe(Effect.runPromise);
-
const doDelete = () => Effect.gen(function* () {
yield* client.deleteAttachment(props.attachment.attachmentId);
yield* pieceCache.update(props.attachment.pieceId, mapProp("attachments", Array.filter((a) => a.attachmentId !== props.attachment.attachmentId)));
@@ -280,7 +265,7 @@ function AttachmentRow(props: AttachmentRow.Props) {
{props.attachment.mediaType === "application/vnd.recordare.musicxml"
|| props.attachment.mediaType === "application/vnd.recordare.musicxml+xml"
|| props.attachment.mediaType === "application/pdf" ? (
-
+
{props.attachment.filename}
) : (