mirror of
https://github.com/luminous-devs/lume.git
synced 2024-10-02 09:50:47 +00:00
polish
This commit is contained in:
parent
5c48ebe103
commit
a3632571ff
@ -80,7 +80,6 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.48.2",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"react-medium-image-zoom": "^5.1.8",
|
||||
"react-router-dom": "^6.18.0",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"reactflow": "^11.10.1",
|
||||
|
@ -191,9 +191,6 @@ dependencies:
|
||||
react-hotkeys-hook:
|
||||
specifier: ^4.4.1
|
||||
version: 4.4.1(react-dom@18.2.0)(react@18.2.0)
|
||||
react-medium-image-zoom:
|
||||
specifier: ^5.1.8
|
||||
version: 5.1.8(react-dom@18.2.0)(react@18.2.0)
|
||||
react-router-dom:
|
||||
specifier: ^6.18.0
|
||||
version: 6.18.0(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -5405,16 +5402,6 @@ packages:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
dev: true
|
||||
|
||||
/react-medium-image-zoom@5.1.8(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-2X4oLlEopIWg7qalR1Qpy4gPrU9CTF0DvJ7HNu5u/NwdyQWupEsje2vuMbjBz7+np8MmQ4DKJ6zGr1ofCuzB3g==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-remove-scroll-bar@2.3.4(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -87,9 +87,9 @@ export function ArticleNoteScreen() {
|
||||
)}
|
||||
<div ref={replyRef} className="px-3">
|
||||
<div className="mb-3 border-b border-neutral-100 pb-3 dark:border-neutral-900">
|
||||
<NoteReplyForm id={id} />
|
||||
<NoteReplyForm eventId={id} />
|
||||
</div>
|
||||
<ReplyList id={id} />
|
||||
<ReplyList eventId={id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,14 +6,7 @@ import { useRef, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { ArrowLeftIcon, CheckCircleIcon, ReplyIcon, ShareIcon } from '@shared/icons';
|
||||
import {
|
||||
ArticleNote,
|
||||
FileNote,
|
||||
NoteActions,
|
||||
NoteReplyForm,
|
||||
TextNote,
|
||||
UnknownNote,
|
||||
} from '@shared/notes';
|
||||
import { MemoizedTextKind, NoteActions, NoteReplyForm, UnknownNote } from '@shared/notes';
|
||||
import { ReplyList } from '@shared/notes/replies/list';
|
||||
import { User } from '@shared/user';
|
||||
|
||||
@ -46,11 +39,7 @@ export function TextNoteScreen() {
|
||||
const renderKind = (event: NDKEvent) => {
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
return <TextNote content={event.content} />;
|
||||
case NDKKind.Article:
|
||||
return <ArticleNote event={event} />;
|
||||
case 1063:
|
||||
return <FileNote event={event} />;
|
||||
return <MemoizedTextKind content={event.content} />;
|
||||
default:
|
||||
return <UnknownNote event={event} />;
|
||||
}
|
||||
@ -106,9 +95,9 @@ export function TextNoteScreen() {
|
||||
)}
|
||||
<div ref={replyRef} className="px-3">
|
||||
<div className="mb-3 border-b border-neutral-100 pb-3 dark:border-neutral-900">
|
||||
<NoteReplyForm id={id} />
|
||||
<NoteReplyForm eventId={id} />
|
||||
</div>
|
||||
<ReplyList id={id} />
|
||||
<ReplyList eventId={id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -83,7 +83,7 @@ export function FileNote({ event }: { event: NDKEvent }) {
|
||||
<div className="mb-3 h-min w-full px-3">
|
||||
<div className="relative flex flex-col gap-2 overflow-hidden rounded-xl bg-neutral-50 pt-3 dark:bg-neutral-950">
|
||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||
<div>{renderFileType()}</div>
|
||||
<div className="relative mt-2">{renderFileType()}</div>
|
||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,15 +1,29 @@
|
||||
import { downloadDir } from '@tauri-apps/api/path';
|
||||
import { Window } from '@tauri-apps/api/window';
|
||||
import { download } from '@tauri-apps/plugin-upload';
|
||||
import { SyntheticEvent } from 'react';
|
||||
import Zoom from 'react-medium-image-zoom';
|
||||
import { SyntheticEvent, useState } from 'react';
|
||||
|
||||
import { CancelIcon, DownloadIcon } from '@shared/icons';
|
||||
import { CheckCircleIcon, DownloadIcon } from '@shared/icons';
|
||||
|
||||
export function ImagePreview({ url }: { url: string }) {
|
||||
const downloadImage = async (url: string) => {
|
||||
const downloadDirPath = await downloadDir();
|
||||
const filename = url.substring(url.lastIndexOf('/') + 1);
|
||||
return await download(url, downloadDirPath + `/${filename}`);
|
||||
const [downloaded, setDownloaded] = useState(false);
|
||||
|
||||
const downloadImage = async (e: { stopPropagation: () => void }) => {
|
||||
try {
|
||||
e.stopPropagation();
|
||||
|
||||
const downloadDirPath = await downloadDir();
|
||||
const filename = url.substring(url.lastIndexOf('/') + 1);
|
||||
await download(url, downloadDirPath + `/${filename}`);
|
||||
|
||||
setDownloaded(true);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const open = () => {
|
||||
return new Window('image-viewer', { url, title: 'Image Viewer' });
|
||||
};
|
||||
|
||||
const fallback = (event: SyntheticEvent<HTMLImageElement, Event>) => {
|
||||
@ -17,25 +31,28 @@ export function ImagePreview({ url }: { url: string }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Zoom key={url} zoomMargin={50} IconUnzoom={() => <CancelIcon className="h-4 w-4" />}>
|
||||
<div className="group relative my-2">
|
||||
<img
|
||||
src={url}
|
||||
alt={url}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
onError={fallback}
|
||||
className="h-auto w-full rounded-lg border border-neutral-300/50 object-cover dark:border-neutral-700/50"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => downloadImage(url)}
|
||||
className="absolute right-2 top-2 hidden h-10 w-10 items-center justify-center rounded-lg bg-black/50 backdrop-blur-xl group-hover:inline-flex hover:bg-blue-500"
|
||||
>
|
||||
<DownloadIcon className="h-4 w-4 text-white" />
|
||||
</button>
|
||||
</div>
|
||||
</Zoom>
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div onClick={open} className="group relative my-2">
|
||||
<img
|
||||
src={url}
|
||||
alt={url}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
onError={fallback}
|
||||
className="h-auto w-full rounded-lg border border-neutral-300/50 object-cover dark:border-neutral-700/50"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => downloadImage(e)}
|
||||
className="absolute right-2 top-2 z-10 hidden h-10 w-10 items-center justify-center rounded-lg bg-blue-500 group-hover:inline-flex hover:bg-blue-600"
|
||||
>
|
||||
{downloaded ? (
|
||||
<CheckCircleIcon className="h-5 w-5 text-white" />
|
||||
) : (
|
||||
<DownloadIcon className="h-5 w-5 text-white" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export function TextNote({ event }: { event: NDKEvent }) {
|
||||
const { addWidget } = useWidget();
|
||||
const { getEventThread } = useNostr();
|
||||
|
||||
const thread = getEventThread(event);
|
||||
const thread = getEventThread(event.tags);
|
||||
|
||||
return (
|
||||
<div className="mb-3 h-min w-full px-3">
|
||||
|
@ -4,6 +4,7 @@ import { WVList } from 'virtua';
|
||||
|
||||
import { LoaderIcon } from '@shared/icons';
|
||||
import {
|
||||
ChildNote,
|
||||
MemoizedArticleKind,
|
||||
MemoizedFileKind,
|
||||
MemoizedTextKind,
|
||||
@ -16,16 +17,33 @@ import { User } from '@shared/user';
|
||||
import { WidgetWrapper } from '@shared/widgets';
|
||||
|
||||
import { useEvent } from '@utils/hooks/useEvent';
|
||||
import { useNostr } from '@utils/hooks/useNostr';
|
||||
import { Widget } from '@utils/types';
|
||||
|
||||
export function ThreadWidget({ widget }: { widget: Widget }) {
|
||||
const { status, data } = useEvent(widget.content);
|
||||
const { getEventThread } = useNostr();
|
||||
|
||||
const renderKind = useCallback(
|
||||
(event: NDKEvent) => {
|
||||
const thread = getEventThread(event.tags);
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
return <MemoizedTextKind content={event.content} />;
|
||||
return (
|
||||
<>
|
||||
{thread ? (
|
||||
<div className="mb-2 w-full px-3">
|
||||
<div className="flex h-min w-full flex-col gap-3 rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||
{thread.rootEventId ? (
|
||||
<ChildNote id={thread.rootEventId} isRoot />
|
||||
) : null}
|
||||
{thread.replyEventId ? <ChildNote id={thread.replyEventId} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<MemoizedTextKind content={event.content} />
|
||||
</>
|
||||
);
|
||||
case NDKKind.Article:
|
||||
return <MemoizedArticleKind id={event.id} tags={event.tags} />;
|
||||
case 1063:
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { NDKEvent, NDKFilter, NDKKind, NDKSubscription } from '@nostr-dev-kit/ndk';
|
||||
import {
|
||||
NDKEvent,
|
||||
NDKFilter,
|
||||
NDKKind,
|
||||
NDKSubscription,
|
||||
NDKTag,
|
||||
} from '@nostr-dev-kit/ndk';
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
import { readBinaryFile } from '@tauri-apps/plugin-fs';
|
||||
import { fetch } from '@tauri-apps/plugin-http';
|
||||
@ -50,20 +56,30 @@ export function useNostr() {
|
||||
}
|
||||
};
|
||||
|
||||
const getEventThread = (event: NDKEvent) => {
|
||||
let rootEventId: string;
|
||||
let replyEventId: string;
|
||||
const getEventThread = (tags: NDKTag[]) => {
|
||||
let rootEventId: string = null;
|
||||
let replyEventId: string = null;
|
||||
|
||||
if (event.tags?.[0]?.[0] === 'e' && !event.tags?.[0]?.[3]) {
|
||||
rootEventId = event.tags[0][1];
|
||||
const events = tags.filter((el) => el[0] === 'e');
|
||||
|
||||
if (!events.length) return null;
|
||||
|
||||
if (events.length === 1)
|
||||
return {
|
||||
rootEventId: events[0][1],
|
||||
replyEventId: null,
|
||||
};
|
||||
|
||||
if (events.length > 1) {
|
||||
rootEventId = events.find((el) => el[3] === 'root')?.[1];
|
||||
replyEventId = events.find((el) => el[3] === 'reply')?.[1];
|
||||
|
||||
if (!rootEventId && !replyEventId) {
|
||||
rootEventId = events[0][1];
|
||||
replyEventId = events[1][1];
|
||||
}
|
||||
}
|
||||
|
||||
rootEventId = event.tags.find((el) => el[3] === 'root')?.[1] || null;
|
||||
// eslint-disable-next-line prefer-const
|
||||
replyEventId = event.tags.find((el) => el[3] === 'reply')?.[1] || null;
|
||||
|
||||
if (!rootEventId && !replyEventId) return null;
|
||||
|
||||
return {
|
||||
rootEventId,
|
||||
replyEventId,
|
||||
|
Loading…
Reference in New Issue
Block a user