Compare commits

...

2 Commits

Author SHA1 Message Date
ea3b353127 chore: bump version 2024-04-17 07:31:32 +07:00
53e62cee80 fix: prevent app crash in some cases 2024-04-17 07:30:52 +07:00
5 changed files with 277 additions and 269 deletions

View File

@@ -1,35 +1,35 @@
{ {
"name": "lume", "name": "lume",
"private": true, "private": true,
"version": "3.0.1", "version": "3.0.2",
"scripts": { "scripts": {
"build": "turbo build", "build": "turbo build",
"dev": "turbo dev", "dev": "turbo dev",
"tauri": "tauri" "tauri": "tauri"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.5.3", "@biomejs/biome": "^1.5.3",
"@tauri-apps/cli": "2.0.0-alpha.21", "@tauri-apps/cli": "2.0.0-alpha.21",
"turbo": "^1.11.3" "turbo": "^1.11.3"
}, },
"packageManager": "pnpm@8.9.0", "packageManager": "pnpm@8.9.0",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.0.0-alpha.14", "@tauri-apps/api": "2.0.0-alpha.14",
"@tauri-apps/plugin-autostart": "2.0.0-alpha.5", "@tauri-apps/plugin-autostart": "2.0.0-alpha.5",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-alpha.5", "@tauri-apps/plugin-clipboard-manager": "2.0.0-alpha.5",
"@tauri-apps/plugin-dialog": "2.0.0-alpha.5", "@tauri-apps/plugin-dialog": "2.0.0-alpha.5",
"@tauri-apps/plugin-fs": "2.0.0-alpha.6", "@tauri-apps/plugin-fs": "2.0.0-alpha.6",
"@tauri-apps/plugin-http": "2.0.0-alpha.6", "@tauri-apps/plugin-http": "2.0.0-alpha.6",
"@tauri-apps/plugin-notification": "2.0.0-alpha.5", "@tauri-apps/plugin-notification": "2.0.0-alpha.5",
"@tauri-apps/plugin-os": "2.0.0-alpha.6", "@tauri-apps/plugin-os": "2.0.0-alpha.6",
"@tauri-apps/plugin-process": "2.0.0-alpha.5", "@tauri-apps/plugin-process": "2.0.0-alpha.5",
"@tauri-apps/plugin-shell": "2.0.0-alpha.5", "@tauri-apps/plugin-shell": "2.0.0-alpha.5",
"@tauri-apps/plugin-sql": "2.0.0-alpha.5", "@tauri-apps/plugin-sql": "2.0.0-alpha.5",
"@tauri-apps/plugin-updater": "2.0.0-alpha.5", "@tauri-apps/plugin-updater": "2.0.0-alpha.5",
"@tauri-apps/plugin-upload": "2.0.0-alpha.5", "@tauri-apps/plugin-upload": "2.0.0-alpha.5",
"million": "^2.6.4" "million": "^2.6.4"
} }
} }

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>
);
} }

View File

@@ -1,125 +1,125 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": { "build": {
"beforeBuildCommand": "pnpm run build", "beforeBuildCommand": "pnpm run build",
"beforeDevCommand": "pnpm run dev", "beforeDevCommand": "pnpm run dev",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist", "distDir": "../dist",
"withGlobalTauri": true "withGlobalTauri": true
}, },
"package": { "package": {
"productName": "Lume", "productName": "Lume",
"version": "3.0.1" "version": "3.0.2"
}, },
"plugins": { "plugins": {
"fs": { "fs": {
"scope": [ "scope": [
"$APPDATA/*", "$APPDATA/*",
"$DATA/*", "$DATA/*",
"$LOCALDATA/*", "$LOCALDATA/*",
"$DESKTOP/*", "$DESKTOP/*",
"$DOCUMENT/*", "$DOCUMENT/*",
"$DOWNLOAD/*", "$DOWNLOAD/*",
"$HOME/*", "$HOME/*",
"$PICTURE/*", "$PICTURE/*",
"$PUBLIC/*", "$PUBLIC/*",
"$VIDEO/*", "$VIDEO/*",
"$RESOURCE", "$RESOURCE",
"$RESOURCE/*", "$RESOURCE/*",
"$RESOURCE/**", "$RESOURCE/**",
"$RESOURCE/locales/*" "$RESOURCE/locales/*"
] ]
}, },
"http": { "http": {
"scope": ["http://**/", "https://**/"] "scope": ["http://**/", "https://**/"]
}, },
"shell": { "shell": {
"open": true, "open": true,
"scope": [] "scope": []
}, },
"updater": { "updater": {
"endpoints": [ "endpoints": [
"https://lus.reya3772.workers.dev/v1/{{target}}/{{arch}}/{{current_version}}", "https://lus.reya3772.workers.dev/v1/{{target}}/{{arch}}/{{current_version}}",
"https://lus.reya3772.workers.dev/{{target}}/{{current_version}}" "https://lus.reya3772.workers.dev/{{target}}/{{current_version}}"
] ]
} }
}, },
"tauri": { "tauri": {
"bundle": { "bundle": {
"active": true, "active": true,
"category": "SocialNetworking", "category": "SocialNetworking",
"deb": { "deb": {
"depends": [] "depends": []
}, },
"externalBin": [], "externalBin": [],
"resources": ["resources/*", "./locales/*"], "resources": ["resources/*", "./locales/*"],
"icon": [ "icon": [
"icons/32x32.png", "icons/32x32.png",
"icons/128x128.png", "icons/128x128.png",
"icons/128x128@2x.png", "icons/128x128@2x.png",
"icons/icon.icns", "icons/icon.icns",
"icons/icon.ico" "icons/icon.ico"
], ],
"copyright": "", "copyright": "",
"identifier": "nu.lume.Lume", "identifier": "nu.lume.Lume",
"longDescription": "nostr client for desktop", "longDescription": "nostr client for desktop",
"shortDescription": "nostr client", "shortDescription": "nostr client",
"targets": "all", "targets": "all",
"updater": { "updater": {
"active": true, "active": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEU3OTdCMkM3RjU5QzE2NzkKUldSNUZwejF4N0tYNTVHYjMrU0JkL090SlEyNUVLYU5TM2hTU3RXSWtEWngrZWJ4a0pydUhXZHEK", "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEU3OTdCMkM3RjU5QzE2NzkKUldSNUZwejF4N0tYNTVHYjMrU0JkL090SlEyNUVLYU5TM2hTU3RXSWtEWngrZWJ4a0pydUhXZHEK",
"windows": { "windows": {
"installMode": "quiet" "installMode": "quiet"
} }
}, },
"appimage": { "appimage": {
"bundleMediaFramework": true "bundleMediaFramework": true
}, },
"rpm": { "rpm": {
"epoch": 0, "epoch": 0,
"files": {}, "files": {},
"release": "1" "release": "1"
}, },
"macOS": { "macOS": {
"entitlements": null, "entitlements": null,
"exceptionDomain": "", "exceptionDomain": "",
"frameworks": [], "frameworks": [],
"license": "../LICENSE", "license": "../LICENSE",
"minimumSystemVersion": "10.15.0", "minimumSystemVersion": "10.15.0",
"providerShortName": null, "providerShortName": null,
"signingIdentity": null "signingIdentity": null
}, },
"windows": { "windows": {
"certificateThumbprint": null, "certificateThumbprint": null,
"digestAlgorithm": "sha256", "digestAlgorithm": "sha256",
"timestampUrl": "" "timestampUrl": ""
} }
}, },
"security": { "security": {
"assetProtocol": { "assetProtocol": {
"enable": true, "enable": true,
"scope": [ "scope": [
"$APPDATA/*", "$APPDATA/*",
"$DATA/*", "$DATA/*",
"$LOCALDATA/*", "$LOCALDATA/*",
"$DESKTOP/*", "$DESKTOP/*",
"$DOCUMENT/*", "$DOCUMENT/*",
"$DOWNLOAD/*", "$DOWNLOAD/*",
"$HOME/*", "$HOME/*",
"$PICTURE/*", "$PICTURE/*",
"$PUBLIC/*", "$PUBLIC/*",
"$VIDEO/*", "$VIDEO/*",
"$APPCONFIG/*", "$APPCONFIG/*",
"$RESOURCE/*" "$RESOURCE/*"
] ]
}, },
"dangerousDisableAssetCspModification": false, "dangerousDisableAssetCspModification": false,
"dangerousRemoteDomainIpcAccess": [], "dangerousRemoteDomainIpcAccess": [],
"freezePrototype": false "freezePrototype": false
}, },
"trayIcon": { "trayIcon": {
"iconPath": "icons/tray.png" "iconPath": "icons/tray.png"
}, },
"macOSPrivateApi": true "macOSPrivateApi": true
} }
} }