fix: prevent app crash in some cases

This commit is contained in:
2024-04-17 07:30:52 +07:00
parent 4c28b4879c
commit 53e62cee80
3 changed files with 121 additions and 113 deletions

View File

@@ -4,39 +4,42 @@ import * as Tooltip from "@radix-ui/react-tooltip";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useColumnContext } from "../../column/provider"; import { useColumnContext } from "../../column/provider";
import { useNoteContext } from "../provider"; import { useNoteContext } from "../provider";
import { toast } from "sonner";
export function NotePin() { export function NotePin() {
const event = useNoteContext(); const { t } = useTranslation();
const { addColumn } = useColumnContext();
const event = useNoteContext();
const { t } = useTranslation(); const pin = async () => {
const { addColumn } = useColumnContext(); if (!event) toast.error("Something is wrong!");
await addColumn({
kind: COL_TYPES.thread,
title: "Thread",
content: event?.id,
});
};
return ( return (
<Tooltip.Provider> <Tooltip.Provider>
<Tooltip.Root delayDuration={150}> <Tooltip.Root delayDuration={150}>
<Tooltip.Trigger asChild> <Tooltip.Trigger asChild>
<button <button
type="button" type="button"
onClick={async () => onClick={() => pin()}
await addColumn({ className="inline-flex items-center justify-center gap-2 pl-2 pr-3 text-sm font-medium rounded-full h-7 w-max bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 dark:bg-neutral-900"
kind: COL_TYPES.thread, >
title: "Thread", <PinIcon className="size-4" />
content: event.id, {t("note.buttons.pin")}
}) </button>
} </Tooltip.Trigger>
className="inline-flex items-center justify-center gap-2 pl-2 pr-3 text-sm font-medium rounded-full h-7 w-max bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 dark:bg-neutral-900" <Tooltip.Portal>
> <Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
<PinIcon className="size-4" /> {t("note.buttons.pinTooltip")}
{t("note.buttons.pin")} <Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</button> </Tooltip.Content>
</Tooltip.Trigger> </Tooltip.Portal>
<Tooltip.Portal> </Tooltip.Root>
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"> </Tooltip.Provider>
{t("note.buttons.pinTooltip")} );
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
);
} }

View File

@@ -3,59 +3,60 @@ import { downloadDir } from "@tauri-apps/api/path";
import { Window } from "@tauri-apps/api/window"; import { Window } from "@tauri-apps/api/window";
import { download } from "@tauri-apps/plugin-upload"; import { download } from "@tauri-apps/plugin-upload";
import { SyntheticEvent, useState } from "react"; import { SyntheticEvent, useState } from "react";
import { useNoteContext } from "../provider";
export function ImagePreview({ url }: { url: string }) { export function ImagePreview({ url }: { url: string }) {
const [downloaded, setDownloaded] = useState(false); const event = useNoteContext();
const [downloaded, setDownloaded] = useState(false);
const downloadImage = async (e: { stopPropagation: () => void }) => { const downloadImage = async (e: { stopPropagation: () => void }) => {
try { try {
e.stopPropagation(); e.stopPropagation();
const downloadDirPath = await downloadDir(); const downloadDirPath = await downloadDir();
const filename = url.substring(url.lastIndexOf("/") + 1); const filename = url.substring(url.lastIndexOf("/") + 1);
await download(url, `${downloadDirPath}/${filename}`); await download(url, `${downloadDirPath}/${filename}`);
setDownloaded(true); setDownloaded(true);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
}; };
const open = async () => { const open = async () => {
const name = new URL(url).pathname.split("/").pop(); return new Window(`image-viewer-${event.id}`, {
return new Window("image-viewer", { url,
url, title: "Image Viewer",
title: name, });
}); };
};
const fallback = (event: SyntheticEvent<HTMLImageElement, Event>) => { const fallback = (event: SyntheticEvent<HTMLImageElement, Event>) => {
event.currentTarget.src = "/fallback-image.jpg"; event.currentTarget.src = "/fallback-image.jpg";
}; };
return ( return (
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> // biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<div onClick={open} className="relative mt-1 mb-2.5 group"> <div onClick={open} className="relative mt-1 mb-2.5 group">
<img <img
src={url} src={url}
alt={url} alt={url}
loading="lazy" loading="lazy"
decoding="async" decoding="async"
style={{ contentVisibility: "auto" }} style={{ contentVisibility: "auto" }}
onError={fallback} onError={fallback}
className="object-cover w-full h-auto border rounded-xl border-neutral-200/50 dark:border-neutral-800/50" className="object-cover w-full h-auto border rounded-xl border-neutral-200/50 dark:border-neutral-800/50"
/> />
<button <button
type="button" type="button"
onClick={(e) => downloadImage(e)} onClick={(e) => downloadImage(e)}
className="absolute z-10 items-center justify-center hidden size-10 bg-white/10 text-black/70 backdrop-blur-xl rounded-lg right-2 top-2 group-hover:inline-flex hover:bg-blue-500 hover:text-white" className="absolute z-10 items-center justify-center hidden size-10 bg-white/10 text-black/70 backdrop-blur-xl rounded-lg right-2 top-2 group-hover:inline-flex hover:bg-blue-500 hover:text-white"
> >
{downloaded ? ( {downloaded ? (
<CheckCircleIcon className="size-5" /> <CheckCircleIcon className="size-5" />
) : ( ) : (
<DownloadIcon className="size-5" /> <DownloadIcon className="size-5" />
)} )}
</button> </button>
</div> </div>
); );
} }

View File

@@ -3,41 +3,45 @@ import { useEvent } from "../../../hooks/useEvent";
import { User } from "../../user"; import { User } from "../../user";
export function ThreadNote({ eventId }: { eventId: string }) { export function ThreadNote({ eventId }: { eventId: string }) {
const { isLoading, data } = useEvent(eventId); const { isLoading, isError, data } = useEvent(eventId);
if (isLoading) { if (isLoading || !data) {
return <div>Loading...</div>; return <div>Loading...</div>;
} }
return ( if (isError) {
<Note.Provider event={data}> return <div>Error</div>;
<Note.Root className="flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950"> }
<div className="flex items-center justify-between px-3 h-16">
<User.Provider pubkey={data.pubkey}> return (
<User.Root className="flex h-16 items-center gap-3 flex-1"> <Note.Provider event={data}>
<User.Avatar className="size-10 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" /> <Note.Root className="flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950">
<div className="flex flex-1 flex-col"> <div className="flex items-center justify-between px-3 h-16">
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" /> <User.Provider pubkey={data.pubkey}>
<div className="inline-flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400"> <User.Root className="flex h-16 items-center gap-3 flex-1">
<User.Time time={data.created_at} /> <User.Avatar className="size-10 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
<span>·</span> <div className="flex flex-1 flex-col">
<User.NIP05 pubkey={data.pubkey} /> <User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
</div> <div className="inline-flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400">
</div> <User.Time time={data.created_at} />
</User.Root> <span>·</span>
</User.Provider> <User.NIP05 pubkey={data.pubkey} />
<Note.Menu /> </div>
</div> </div>
<Note.Thread className="mb-2" /> </User.Root>
<Note.Content className="min-w-0 px-3" /> </User.Provider>
<div className="flex items-center justify-between px-3 h-14"> <Note.Menu />
<Note.Pin /> </div>
<div className="inline-flex items-center gap-4"> <Note.Thread className="mb-2" />
<Note.Repost /> <Note.Content className="min-w-0 px-3" />
<Note.Zap /> <div className="flex items-center justify-between px-3 h-14">
</div> <Note.Pin />
</div> <div className="inline-flex items-center gap-4">
</Note.Root> <Note.Repost />
</Note.Provider> <Note.Zap />
); </div>
</div>
</Note.Root>
</Note.Provider>
);
} }