refactor: lint formatting and fixes #6

Merged
me merged 10 commits from refactor/lint_fixes into main 2026-05-08 06:13:25 +00:00
2 changed files with 772 additions and 767 deletions
Showing only changes of commit 7e79c6ca8b - Show all commits
+66 -64
View File
@@ -11,6 +11,7 @@ import {
useParams, useParams,
} from "react-router-dom"; } from "react-router-dom";
import { api } from "../api/apiClient"; import { api } from "../api/apiClient";
import type { LetterResponseData } from "../api/response";
import { import {
type CanvasStyle, type CanvasStyle,
type CanvasTools, type CanvasTools,
@@ -26,7 +27,6 @@ import DateDisplay from "../components/ui/DateDisplay";
import { LogModal } from "../components/ui/LogModal"; import { LogModal } from "../components/ui/LogModal";
import { Modal } from "../components/ui/Modal"; import { Modal } from "../components/ui/Modal";
import { Navbar } from "../components/ui/Navbar"; import { Navbar } from "../components/ui/Navbar";
import { endpoints } from "../config/endpoints"; import { endpoints } from "../config/endpoints";
import { PATHS } from "../config/routes"; import { PATHS } from "../config/routes";
import { useKeyStore } from "../store/useKeyStore"; import { useKeyStore } from "../store/useKeyStore";
@@ -116,26 +116,11 @@ export default function Editor() {
justSavedRef.current = false; justSavedRef.current = false;
return; return;
} }
const loadExistingLetter = async () => { const decryptAndLoadLetter = async (
setIsInitialLoading(true); letterData: LetterResponseData,
masterKey: CryptoKey,
) => {
const cryptoUtils = new CryptoUtils(); const cryptoUtils = new CryptoUtils();
try {
const res = await api.get(`${endpoints.LETTERS}${public_id}/`);
const letterData = res.data;
setLastSaved(formatRelativeDate(new Date(letterData.updated_at)));
setLetterStatus(letterData.status);
if (letterData.status === "SEALED") {
navigateRef.current(PATHS.read(public_id), { replace: true });
return;
}
if (!letterData.encrypted_dek) {
return;
}
const metadata = await cryptoUtils.decryptMetadata( const metadata = await cryptoUtils.decryptMetadata(
{ {
encrypted_content: letterData.encrypted_metadata, encrypted_content: letterData.encrypted_metadata,
@@ -167,8 +152,7 @@ export default function Editor() {
if (isPartialFailure) { if (isPartialFailure) {
setDecryptionStatus({ setDecryptionStatus({
status: "WARN", status: "WARN",
message: message: "Failed to decrypt some elements. Please check the render.",
"Failed to decrypt some elements. Please check the render.",
log: errors.toString(), log: errors.toString(),
}); });
} }
@@ -176,11 +160,30 @@ export default function Editor() {
if (canvasRef.current) { if (canvasRef.current) {
await canvasRef.current.loadData(canvasDataWithDecryptedImages); await canvasRef.current.loadData(canvasDataWithDecryptedImages);
} }
} catch (_err) { };
const loadExistingLetter = async () => {
setIsInitialLoading(true);
try {
const res = await api.get(`${endpoints.LETTERS}${public_id}/`);
const letterData = res.data;
setLastSaved(formatRelativeDate(new Date(letterData.updated_at)));
setLetterStatus(letterData.status);
if (letterData.status === "SEALED") {
navigateRef.current(PATHS.read(public_id), { replace: true });
return;
}
if (letterData.encrypted_dek && masterKey) {
await decryptAndLoadLetter(letterData, masterKey);
}
} catch (err) {
setDecryptionStatus({ setDecryptionStatus({
status: "ERROR", status: "ERROR",
message: "Failed to decrypt letter. Please try again later.", message: "Failed to decrypt letter. Please try again later.",
log: _err instanceof Error ? _err.message : "Unknown error", log: err instanceof Error ? err.message : "Unknown error",
}); });
} finally { } finally {
setIsInitialLoading(false); setIsInitialLoading(false);
@@ -242,76 +245,79 @@ export default function Editor() {
} }
}; };
const handleSave = async ( const getRequestData = async (
status: "SEALED" | "DRAFT" | "VAULT", targetId: string,
status: string,
vaultDate?: Date, vaultDate?: Date,
): Promise<void> => { ): Promise<FormData> => {
setSealBtnClicked(false);
let targetId = public_id || letterIdRef.current;
if (!targetId) {
targetId = crypto.randomUUID();
}
if (saveOverlay === "SAVING" || !masterKey) return;
setSaveOverlay("SAVING");
setShowSaveOverlay(true);
const cryptoUtils = new CryptoUtils(); const cryptoUtils = new CryptoUtils();
await cryptoUtils.initialize(); await cryptoUtils.initialize();
try { const canvasData = (await canvasRef.current?.getData()) || { objects: [] };
const canvasData = (await canvasRef.current?.getData()) || {
objects: [],
};
const canvasImages = canvasRef.current?.getImages() || []; const canvasImages = canvasRef.current?.getImages() || [];
const { encryptedImageFiles, encryptedCanvasData } = const { encryptedImageFiles, encryptedCanvasData } =
await encryptCanvasImages( await encryptCanvasImages(
canvasData, canvasData,
canvasImages, canvasImages,
masterKey, // biome-ignore lint/style/noNonNullAssertion: masterkey can never be null here
masterKey!,
cryptoUtils, cryptoUtils,
); );
const encrypted_letter = await cryptoUtils.encryptLetter( const encrypted_letter = await cryptoUtils.encryptLetter(
JSON.stringify(encryptedCanvasData), JSON.stringify(encryptedCanvasData),
masterKey, // biome-ignore lint/style/noNonNullAssertion: masterkey can never be null here
masterKey!,
); );
const encrypted_metadata = await cryptoUtils.encryptMetadata( const encrypted_metadata = await cryptoUtils.encryptMetadata(
{ recipient, tags: [] }, { recipient, tags: [] },
masterKey, // biome-ignore lint/style/noNonNullAssertion: masterkey can never be null here
masterKey!,
); );
const formData = new FormData(); const formData = new FormData();
if (status === "VAULT") { if (status === "VAULT") {
const finalDate = vaultDate || unlockDate; const finalDate = vaultDate || unlockDate;
formData.append("type", "VAULT"); formData.append("type", "VAULT");
if (finalDate) { if (finalDate) formData.append("unlock_at", finalDate.toISOString());
formData.append("unlock_at", finalDate.toISOString());
}
formData.append("status", "SEALED"); formData.append("status", "SEALED");
} else { } else {
formData.append("type", "KEPT"); formData.append("type", "KEPT");
formData.append("status", status); formData.append("status", status);
} }
formData.append("public_id", targetId); formData.append("public_id", targetId);
formData.append("encrypted_content", encrypted_letter.encrypted_content); formData.append("encrypted_content", encrypted_letter.encrypted_content);
formData.append("encrypted_dek", encrypted_letter.encrypted_dek); formData.append("encrypted_dek", encrypted_letter.encrypted_dek);
formData.append( formData.append("encrypted_metadata", encrypted_metadata.encrypted_content);
"encrypted_metadata",
encrypted_metadata.encrypted_content,
);
encryptedImageFiles.forEach((blob, filename) => { encryptedImageFiles.forEach((blob, filename) => {
formData.append("image_files", blob, filename); formData.append("image_files", blob, filename);
}); });
await api.put(`${endpoints.LETTERS}${targetId}/`, formData); return formData;
justSavedRef.current = true; };
const handleSave = async (
status: "SEALED" | "DRAFT" | "VAULT",
vaultDate?: Date,
): Promise<void> => {
setSealBtnClicked(false);
// use the letter's id if an existing letter or create a new id
const targetId = public_id || letterIdRef.current || crypto.randomUUID();
if (saveOverlay === "SAVING" || !masterKey) return;
setSaveOverlay("SAVING");
setShowSaveOverlay(true);
try {
const formData = await getRequestData(targetId, status, vaultDate);
await api.put(`${endpoints.LETTERS}${targetId}/`, formData);
justSavedRef.current = true;
if (!public_id) { if (!public_id) {
letterIdRef.current = targetId; letterIdRef.current = targetId;
navigate(PATHS.write(targetId), { replace: true }); navigate(PATHS.write(targetId), { replace: true });
@@ -326,7 +332,7 @@ export default function Editor() {
} }
setSaveOverlay("SAVED"); setSaveOverlay("SAVED");
setShowSaveOverlay(true); setShowSaveOverlay(true);
} catch (_error) { } catch {
setSaveOverlay("ERROR"); setSaveOverlay("ERROR");
setShowSaveOverlay(true); setShowSaveOverlay(true);
} }
@@ -337,8 +343,7 @@ export default function Editor() {
<Navbar <Navbar
child={ child={
<div <div
className={`flex items-center gap-2 ${ className={`flex items-center gap-2 ${isSaveDatePulsing ? "animate-pulse" : ""
isSaveDatePulsing ? "animate-pulse" : ""
}`} }`}
> >
<div className="text-xxs text-neutral-content/30 flex-col justify-end leading-none text-right"> <div className="text-xxs text-neutral-content/30 flex-col justify-end leading-none text-right">
@@ -391,8 +396,7 @@ export default function Editor() {
{saveOverlay === "SAVING" && ( {saveOverlay === "SAVING" && (
<div <div
role="alert" role="alert"
className={`alert text-center alert-neutral shadow-lg transition-all ease-in-out duration-2000 ${ className={`alert text-center alert-neutral shadow-lg transition-all ease-in-out duration-2000 ${showSaveOverlay
showSaveOverlay
? "opacity-100 scale-100 translate-y-0" ? "opacity-100 scale-100 translate-y-0"
: "opacity-0 scale-95 translate-y-1" : "opacity-0 scale-95 translate-y-1"
}`} }`}
@@ -410,8 +414,7 @@ export default function Editor() {
<div <div
role="alert" role="alert"
data-testid="save-success-toast" data-testid="save-success-toast"
className={`alert alert-success shadow-lg transition-all ease-in-out duration-2000 ${ className={`alert alert-success shadow-lg transition-all ease-in-out duration-2000 ${showSaveOverlay
showSaveOverlay
? "opacity-100 scale-100 translate-y-0" ? "opacity-100 scale-100 translate-y-0"
: "opacity-0 scale-95 translate-y-1" : "opacity-0 scale-95 translate-y-1"
}`} }`}
@@ -424,8 +427,7 @@ export default function Editor() {
{saveOverlay === "ERROR" && ( {saveOverlay === "ERROR" && (
<div <div
role="alert" role="alert"
className={`alert alert-error shadow-lg transition-all duration-300 ${ className={`alert alert-error shadow-lg transition-all duration-300 ${showSaveOverlay
showSaveOverlay
? "opacity-100 scale-100 translate-y-0" ? "opacity-100 scale-100 translate-y-0"
: "opacity-0 scale-95 translate-y-1" : "opacity-0 scale-95 translate-y-1"
}`} }`}
+7 -4
View File
@@ -73,7 +73,8 @@ export default function Reader() {
const key = await cryptoUtils.extractSharingKey(encryptedDek, masterKey); const key = await cryptoUtils.extractSharingKey(encryptedDek, masterKey);
try { try {
await api.patch(`${endpoints.LETTERS}${public_id}/`, { type: "SENT" }); await api.patch(`${endpoints.LETTERS}${public_id}/`, { type: "SENT" });
} catch (_err) { } catch {
// shouldn't obstruct share if api operation fails (since it's client side share)
} finally { } finally {
setShareLink(`${window.location.origin}${PATHS.read(public_id)}#${key}`); setShareLink(`${window.location.origin}${PATHS.read(public_id)}#${key}`);
} }
@@ -86,7 +87,10 @@ export default function Reader() {
await api.patch(`${endpoints.LETTERS}${public_id}/`, { await api.patch(`${endpoints.LETTERS}${public_id}/`, {
status: "BURNED", status: "BURNED",
}); });
} catch (_err) { } catch {
// should not obstruct burn if api operation fails
// WHY?: it disconnects the UX. if you want to burn the letter, you should be able to burn the letter
// TODO: maybe say something like: "the wind is strong today, let's try again"? or maybe something less stupid :3
} finally { } finally {
setIsBurning(false); setIsBurning(false);
setShowBurnModal(false); setShowBurnModal(false);
@@ -268,8 +272,7 @@ export default function Reader() {
<section className="min-h-fit w-full bg-base-100 px-4 py-8 md:py-16 font-serif relative overflow-hidden"> <section className="min-h-fit w-full bg-base-100 px-4 py-8 md:py-16 font-serif relative overflow-hidden">
<div className="fixed inset-0 bg-vig pointer-events-none z-0" /> <div className="fixed inset-0 bg-vig pointer-events-none z-0" />
<div <div
className={`transition-all delay-300 duration-1000 relative ${ className={`transition-all delay-300 duration-1000 relative ${revealState === "REVEALED"
revealState === "REVEALED"
? "opacity-0 w-0 h-0 overflow-hidden invisible" ? "opacity-0 w-0 h-0 overflow-hidden invisible"
: "opacity-100" : "opacity-100"
}`} }`}