mirror of
https://github.com/luminous-devs/lume.git
synced 2024-10-01 01:10:48 +00:00
feat: polish
This commit is contained in:
parent
72870bb131
commit
ab27bd5f44
@ -1,5 +1,5 @@
|
|||||||
import { useArk } from "@lume/ark";
|
import { useArk } from "@lume/ark";
|
||||||
import { LoaderIcon } from "@lume/icons";
|
import { ArrowRightCircleIcon, LoaderIcon } from "@lume/icons";
|
||||||
import { FETCH_LIMIT } from "@lume/utils";
|
import { FETCH_LIMIT } from "@lume/utils";
|
||||||
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
||||||
import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
|
import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
@ -25,13 +25,10 @@ export function ActivityList() {
|
|||||||
}) => {
|
}) => {
|
||||||
const events = await ark.getInfiniteEvents({
|
const events = await ark.getInfiniteEvents({
|
||||||
filter: {
|
filter: {
|
||||||
kinds: [NDKKind.Zap],
|
kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Zap],
|
||||||
"#p": [
|
"#p": [ark.account.pubkey],
|
||||||
"126103bfddc8df256b6e0abfd7f3797c80dcc4ea88f7c2f87dd4104220b4d65f",
|
|
||||||
ark.account.pubkey,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
limit: 200,
|
limit: FETCH_LIMIT,
|
||||||
pageParam,
|
pageParam,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
@ -84,12 +81,36 @@ export function ActivityList() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col flex-1 min-h-0 overflow-y-auto">
|
<div className="flex flex-col flex-1 min-h-0 overflow-y-auto">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="h-24 flex items-center justify-center">
|
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<LoaderIcon className="size-5 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
|
) : !allEvents.length ? (
|
||||||
|
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||||
|
<p className="mb-2 text-2xl">🎉</p>
|
||||||
|
<p className="text-center font-medium">Yo! Nothing new yet.</p>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
allEvents.map((event) => renderEvenKind(event))
|
allEvents.map((event) => renderEvenKind(event))
|
||||||
)}
|
)}
|
||||||
|
<div className="flex items-center justify-center h-16">
|
||||||
|
{hasNextPage ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => fetchNextPage()}
|
||||||
|
disabled={!hasNextPage || isFetchingNextPage}
|
||||||
|
className="inline-flex items-center justify-center w-full h-12 gap-2 font-medium bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800 rounded-xl focus:outline-none"
|
||||||
|
>
|
||||||
|
{isFetchingNextPage ? (
|
||||||
|
<LoaderIcon className="size-5 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
Load more
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { ActivityList } from "./components/list";
|
|||||||
|
|
||||||
export function ActivityScreen() {
|
export function ActivityScreen() {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full rounded-xl shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:shadow-[inset_0_0_0.5px_1px_hsla(0,0%,100%,0.075),0_0_0_1px_hsla(0,0%,0%,0.05),0_0.3px_0.4px_hsla(0,0%,0%,0.02),0_0.9px_1.5px_hsla(0,0%,0%,0.045),0_3.5px_6px_hsla(0,0%,0%,0.09)]">
|
<div className="flex h-full w-full rounded-xl shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:shadow-none dark:ring-1 dark:ring-white/10">
|
||||||
<div className="h-full flex flex-col w-96 shrink-0 rounded-l-xl bg-white/50 backdrop-blur-xl dark:bg-black/50">
|
<div className="h-full flex flex-col w-96 shrink-0 rounded-l-xl bg-white/50 backdrop-blur-xl dark:bg-black/50">
|
||||||
<div className="h-14 shrink-0 flex items-center px-5 text-lg font-semibold border-b border-black/10 dark:border-white/10">
|
<div className="h-14 shrink-0 flex items-center px-5 text-lg font-semibold border-b border-black/10 dark:border-white/10">
|
||||||
Activity
|
Activity
|
||||||
|
@ -552,9 +552,11 @@ export class Ark {
|
|||||||
if (!res.ok) throw new Error(`Failed to fetch NIP-05 service: ${nip05}`);
|
if (!res.ok) throw new Error(`Failed to fetch NIP-05 service: ${nip05}`);
|
||||||
|
|
||||||
const data: NIP05 = await res.json();
|
const data: NIP05 = await res.json();
|
||||||
|
|
||||||
if (!data.names) return false;
|
if (!data.names) return false;
|
||||||
if (data.names[localPath.toLowerCase()] === pubkey) return true;
|
if (data.names[localPath.toLowerCase()] === pubkey) return true;
|
||||||
if (data.names[localPath] === pubkey) return true;
|
if (data.names[localPath] === pubkey) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,17 +12,16 @@ export function ThreadNote({ eventId }: { eventId: string }) {
|
|||||||
return (
|
return (
|
||||||
<Note.Provider event={data}>
|
<Note.Provider event={data}>
|
||||||
<Note.Root>
|
<Note.Root>
|
||||||
<div className="flex items-center justify-between px-3 h-14">
|
<div className="flex items-center justify-between px-3 h-16">
|
||||||
<Note.User className="flex-1 pr-1" />
|
|
||||||
<User.Provider pubkey={data.pubkey}>
|
<User.Provider pubkey={data.pubkey}>
|
||||||
<User.Root className="flex h-16 items-center gap-3 px-3 flex-1">
|
<User.Root className="flex h-16 items-center gap-3 flex-1">
|
||||||
<User.Avatar className="size-10 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
|
<User.Avatar className="size-10 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
|
||||||
<div className="flex flex-1 flex-col">
|
<div className="flex flex-1 flex-col">
|
||||||
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
|
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
|
||||||
<div className="inline-flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400">
|
<div className="inline-flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400">
|
||||||
<User.Time time={data.created_at} />
|
<User.Time time={data.created_at} />
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<User.NIP05 />
|
<User.NIP05 pubkey={data.pubkey} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</User.Root>
|
</User.Root>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import * as Avatar from "@radix-ui/react-avatar";
|
import * as Avatar from "@radix-ui/react-avatar";
|
||||||
import { minidenticon } from "minidenticons";
|
import { minidenticon } from "minidenticons";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useUserContext } from "./provider";
|
import { useUserContext } from "./provider";
|
||||||
|
|
||||||
@ -9,7 +10,7 @@ export function UserAvatar({ className }: { className?: string }) {
|
|||||||
const fallbackAvatar = useMemo(
|
const fallbackAvatar = useMemo(
|
||||||
() =>
|
() =>
|
||||||
`data:image/svg+xml;utf8,${encodeURIComponent(
|
`data:image/svg+xml;utf8,${encodeURIComponent(
|
||||||
minidenticon(user?.pubkey, 90, 50),
|
minidenticon(user?.pubkey || nanoid(), 90, 50),
|
||||||
)}`,
|
)}`,
|
||||||
[user],
|
[user],
|
||||||
);
|
);
|
||||||
|
@ -4,19 +4,25 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
import { useArk } from "../../hooks/useArk";
|
import { useArk } from "../../hooks/useArk";
|
||||||
import { useUserContext } from "./provider";
|
import { useUserContext } from "./provider";
|
||||||
|
|
||||||
export function UserNip05({ className }: { className?: string }) {
|
export function UserNip05({
|
||||||
|
pubkey,
|
||||||
|
className,
|
||||||
|
}: { pubkey: string; className?: string }) {
|
||||||
const ark = useArk();
|
const ark = useArk();
|
||||||
const user = useUserContext();
|
const user = useUserContext();
|
||||||
|
|
||||||
const { isLoading, data: verified } = useQuery({
|
const { isLoading, data: verified } = useQuery({
|
||||||
queryKey: ["nip05", user?.nip05],
|
queryKey: ["nip05", user?.nip05],
|
||||||
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||||
|
if (!user) return false;
|
||||||
|
if (!user.nip05) return false;
|
||||||
return ark.validateNIP05({
|
return ark.validateNIP05({
|
||||||
pubkey: user?.pubkey,
|
pubkey,
|
||||||
nip05: user?.nip05,
|
nip05: user.nip05,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
enabled: !!user,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@ -38,10 +44,8 @@ export function UserNip05({ className }: { className?: string }) {
|
|||||||
: user.nip05}
|
: user.nip05}
|
||||||
</p>
|
</p>
|
||||||
{!isLoading && verified ? (
|
{!isLoading && verified ? (
|
||||||
<VerifiedIcon className="size-5 text-teal-500" />
|
<VerifiedIcon className="size-4 text-teal-500" />
|
||||||
) : (
|
) : null}
|
||||||
<UnverifiedIcon className="size-5 text-red-500" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { SVGProps } from 'react';
|
import { SVGProps } from "react";
|
||||||
|
|
||||||
export function VerifiedIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
export function VerifiedIcon(
|
||||||
return (
|
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
|
||||||
<svg
|
) {
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
return (
|
||||||
width="24"
|
<svg
|
||||||
height="24"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
width="24"
|
||||||
viewBox="0 0 24 24"
|
height="24"
|
||||||
{...props}
|
fill="none"
|
||||||
>
|
viewBox="0 0 24 24"
|
||||||
<path
|
{...props}
|
||||||
fill="currentColor"
|
>
|
||||||
fillRule="evenodd"
|
<path
|
||||||
d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm3.58 7.975a.75.75 0 00-1.16-.95l-3.976 4.859L9.03 12.47a.75.75 0 00-1.06 1.06l2 2a.75.75 0 001.11-.055l4.5-5.5z"
|
fill="currentColor"
|
||||||
clipRule="evenodd"
|
d="M8.522 3.587C9.32 2.491 10.557 1.75 12 1.75c1.44 0 2.684.74 3.486 1.837 1.34-.21 2.746.145 3.764 1.163 1.018 1.019 1.373 2.425 1.163 3.767 1.094.802 1.837 2.039 1.837 3.483s-.743 2.68-1.837 3.483c.21 1.342-.145 2.748-1.163 3.767-1.021 1.02-2.427 1.365-3.762 1.16-.801 1.1-2.046 1.84-3.488 1.84-1.446 0-2.683-.744-3.485-1.84-1.337.206-2.743-.139-3.765-1.16-1.02-1.021-1.366-2.429-1.154-3.767-1.094-.8-1.846-2.036-1.846-3.483s.752-2.682 1.846-3.483c-.212-1.338.133-2.746 1.154-3.767 1.02-1.02 2.426-1.373 3.772-1.163zm7.042 7.094a1 1 0 10-1.128-1.652l-.087.06a13.844 13.844 0 00-3.517 3.468l-1.125-1.124a1 1 0 00-1.414 1.415l2.007 2.004a1 1 0 001.575-.21 11.843 11.843 0 013.602-3.902l.087-.06z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export function AppLayout({ platform }: { platform: Platform }) {
|
|||||||
platform !== "macos" ? "bg-blue-50 dark:bg-blue-950" : "",
|
platform !== "macos" ? "bg-blue-50 dark:bg-blue-950" : "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{platform !== "macos" ? (
|
{platform === "windows" ? (
|
||||||
<WindowTitleBar platform={platform} />
|
<WindowTitleBar platform={platform} />
|
||||||
) : (
|
) : (
|
||||||
<div data-tauri-drag-region className="h-9 shrink-0" />
|
<div data-tauri-drag-region className="h-9 shrink-0" />
|
||||||
|
@ -11,7 +11,7 @@ export function AuthLayout({ platform }: { platform: Platform }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-screen h-screen bg-black text-neutral-50">
|
<div className="flex flex-col w-screen h-screen bg-black text-neutral-50">
|
||||||
{platform !== "macos" ? (
|
{platform === "windows" ? (
|
||||||
<WindowTitleBar platform={platform} />
|
<WindowTitleBar platform={platform} />
|
||||||
) : (
|
) : (
|
||||||
<div data-tauri-drag-region className="h-9 shrink-0" />
|
<div data-tauri-drag-region className="h-9 shrink-0" />
|
||||||
|
@ -82,24 +82,6 @@ export function Navigation() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink
|
|
||||||
to="/nwc"
|
|
||||||
preventScrollReset={true}
|
|
||||||
className="inline-flex flex-col items-center justify-center"
|
|
||||||
>
|
|
||||||
{({ isActive }) => (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"inline-flex aspect-square h-auto w-full items-center justify-center rounded-xl",
|
|
||||||
isActive
|
|
||||||
? "bg-black/10 text-black dark:bg-white/10 dark:text-white"
|
|
||||||
: "text-black/50 dark:text-neutral-400",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<NwcIcon className="size-6 rotate-12" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
@ -11,10 +11,7 @@
|
|||||||
"title": "Lume",
|
"title": "Lume",
|
||||||
"center": true,
|
"center": true,
|
||||||
"fullscreen": false,
|
"fullscreen": false,
|
||||||
"hiddenTitle": true,
|
"fileDropEnabled": true
|
||||||
"fileDropEnabled": true,
|
|
||||||
"decorations": false,
|
|
||||||
"transparent": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user