1
0
mirror of https://github.com/lumehq/lume.git synced 2025-03-29 11:11:43 +01:00

Merge pull request from lumehq/v4/home-screen

v4/home-screen
This commit is contained in:
Ren Amamiya 2024-03-09 10:09:29 +07:00 committed by GitHub
commit 0c4b309a11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 766 additions and 238 deletions

@ -24,7 +24,7 @@ input::-ms-clear {
}
media-controller {
@apply w-full;
@apply w-full overflow-hidden rounded-xl;
}
@layer utilities {

@ -12,6 +12,7 @@ import { locale, platform } from "@tauri-apps/plugin-os";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
import { routeTree } from "./router.gen"; // auto generated file
import { CancelCircleIcon, CheckCircleIcon, InfoCircleIcon } from "@lume/icons";
const queryClient = new QueryClient({
defaultOptions: {
@ -72,7 +73,16 @@ if (!rootElement.innerHTML) {
persistOptions={{ persister }}
>
<StrictMode>
<Toaster position="top-center" richColors />
<Toaster
position="bottom-right"
icons={{
success: <CheckCircleIcon className="size-5" />,
info: <InfoCircleIcon className="size-5" />,
error: <CancelCircleIcon className="size-5" />,
}}
closeButton
theme="system"
/>
<App />
</StrictMode>
</PersistQueryClientProvider>

@ -45,7 +45,8 @@ function Inactive({ pubkey }: { pubkey: string }) {
const changeAccount = async (npub: string) => {
const select = await ark.load_selected_account(npub);
if (select) navigate({ to: "/$account/home", params: { account: npub } });
if (select)
navigate({ to: "/$account/home/local", params: { account: npub } });
};
return (

@ -104,7 +104,7 @@ export function BackupDialog() {
</button>
) : (
<Link
to="/$account/home"
to="/$account/home/local"
params={{ account }}
search={{ guest: false }}
className="inline-flex h-11 w-full items-center justify-center gap-1.5 rounded-lg bg-blue-500 px-5 font-medium text-white hover:bg-blue-600"

@ -68,7 +68,7 @@ function Navigation() {
data-tauri-drag-region
className="flex h-full flex-1 items-center gap-2"
>
<Link to="/$account/home" params={{ account }}>
<Link to="/$account/home/local" params={{ account }}>
{({ isActive }) => (
<div
className={cn(

@ -1,133 +0,0 @@
import { RepostNote } from "@/components/repost";
import { Suggest } from "@/components/suggest";
import { TextNote } from "@/components/text";
import { useArk } from "@lume/ark";
import {
LoaderIcon,
ArrowRightCircleIcon,
RefreshIcon,
InfoIcon,
} from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { EmptyFeed } from "@lume/ui";
import { FETCH_LIMIT } from "@lume/utils";
import { useInfiniteQuery } from "@tanstack/react-query";
import { createLazyFileRoute } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
export const Route = createLazyFileRoute("/$account/home")({
component: Home,
});
function Home() {
const ark = useArk();
const currentDate = new Date().toLocaleString("default", {
weekday: "long",
month: "long",
day: "numeric",
});
const { account } = Route.useParams();
const {
data,
hasNextPage,
isLoading,
isRefetching,
isFetchingNextPage,
fetchNextPage,
refetch,
} = useInfiniteQuery({
queryKey: ["local_newsfeed", account],
initialPageParam: 0,
queryFn: async ({ pageParam }: { pageParam: number }) => {
const events = await ark.get_events(
"local",
FETCH_LIMIT,
pageParam,
true,
);
return events;
},
getNextPageParam: (lastPage) => {
const lastEvent = lastPage?.at(-1);
if (!lastEvent) return;
return lastEvent.created_at - 1;
},
select: (data) => data?.pages.flatMap((page) => page),
refetchOnWindowFocus: false,
});
const renderItem = (event: Event) => {
if (!event) return;
switch (event.kind) {
case Kind.Repost:
return <RepostNote key={event.id} event={event} />;
default:
return <TextNote key={event.id} event={event} />;
}
};
return (
<div className="flex flex-col gap-3">
<div className="mx-auto flex h-12 w-full max-w-xl shrink-0 items-center justify-between border-b border-neutral-100 dark:border-neutral-900">
<h3 className="text-sm font-medium uppercase leading-tight text-neutral-600 dark:text-neutral-400">
{currentDate}
</h3>
<div className="flex items-center gap-3">
<button
type="button"
onClick={() => refetch()}
className="text-neutral-700 hover:text-blue-500 dark:text-neutral-300"
>
<RefreshIcon className="size-4" />
</button>
</div>
</div>
<div className="mx-auto flex w-full max-w-xl flex-1 flex-col">
<div className="flex-1">
{isLoading || isRefetching ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<LoaderIcon className="size-5 animate-spin" />
</div>
) : !data.length ? (
<div className="flex flex-col gap-3">
<div className="flex items-center gap-2 rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900">
<InfoIcon className="size-5" />
<p>
Empty newsfeed. Or you can go to{" "}
<a href="" className="text-blue-500 hover:text-blue-600">
Discover
</a>
</p>
</div>
<Suggest />
</div>
) : (
<Virtualizer overscan={3}>
{data.map((item) => renderItem(item))}
</Virtualizer>
)}
<div className="flex h-20 items-center justify-center">
{hasNextPage ? (
<button
type="button"
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />
Load more
</>
)}
</button>
) : null}
</div>
</div>
</div>
</div>
);
}

@ -0,0 +1,68 @@
import { GlobalIcon, LocalIcon, RefreshIcon } from "@lume/icons";
import { cn } from "@lume/utils";
import { useQueryClient } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { Outlet, createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/$account/home")({
component: Screen,
});
function Screen() {
const queryClient = useQueryClient();
const { account } = Route.useParams();
const refresh = async () => {
const queryKey = `${window.location.pathname.split("/").at(-1)}_newsfeed`;
await queryClient.refetchQueries({ queryKey: [queryKey, account] });
};
return (
<div className="flex flex-col">
<div className="mx-auto mb-4 flex h-16 w-full max-w-xl shrink-0 items-center justify-between border-b border-neutral-100 dark:border-neutral-900">
<div className="flex items-center gap-2">
<Link to="/$account/home/local">
{({ isActive }) => (
<div
className={cn(
"inline-flex items-center justify-center gap-2 rounded-full px-4 py-2 text-sm leading-tight hover:bg-neutral-100 dark:hover:bg-neutral-900",
isActive
? "bg-neutral-100 font-semibold text-neutral-900 hover:bg-neutral-200 dark:bg-neutral-900 dark:text-neutral-100 dark:hover:bg-neutral-800"
: "text-neutral-600 dark:text-neutral-400",
)}
>
<LocalIcon className="size-4" />
Local
</div>
)}
</Link>
<Link to="/$account/home/global">
{({ isActive }) => (
<div
className={cn(
"inline-flex items-center justify-center gap-2 rounded-full px-4 py-2 text-sm leading-tight hover:bg-neutral-100 dark:hover:bg-neutral-900",
isActive
? "bg-neutral-100 font-semibold text-neutral-900 hover:bg-neutral-200 dark:bg-neutral-900 dark:text-neutral-100 dark:hover:bg-neutral-800"
: "text-neutral-600 dark:text-neutral-400",
)}
>
<GlobalIcon className="size-4" />
Global
</div>
)}
</Link>
</div>
<div className="flex items-center gap-3">
<button
type="button"
onClick={refresh}
className="text-neutral-700 hover:text-blue-500 dark:text-neutral-300"
>
<RefreshIcon className="size-4" />
</button>
</div>
</div>
<Outlet />
</div>
);
}

@ -0,0 +1,91 @@
import { RepostNote } from "@/components/repost";
import { Suggest } from "@/components/suggest";
import { TextNote } from "@/components/text";
import { useArk } from "@lume/ark";
import { LoaderIcon, ArrowRightCircleIcon, InfoIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { FETCH_LIMIT } from "@lume/utils";
import { useInfiniteQuery } from "@tanstack/react-query";
import { createLazyFileRoute } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
export const Route = createLazyFileRoute("/$account/home/global")({
component: Screen,
});
function Screen() {
const ark = useArk();
const { account } = Route.useParams();
const {
data,
hasNextPage,
isLoading,
isRefetching,
isFetchingNextPage,
fetchNextPage,
} = useInfiniteQuery({
queryKey: ["global_newsfeed", account],
initialPageParam: 0,
queryFn: async ({ pageParam }: { pageParam: number }) => {
const events = await ark.get_events(
"global",
FETCH_LIMIT,
pageParam,
true,
);
return events;
},
getNextPageParam: (lastPage) => {
const lastEvent = lastPage?.at(-1);
if (!lastEvent) return;
return lastEvent.created_at - 1;
},
select: (data) => data?.pages.flatMap((page) => page),
refetchOnWindowFocus: false,
});
const renderItem = (event: Event) => {
if (!event) return;
switch (event.kind) {
case Kind.Repost:
return <RepostNote key={event.id} event={event} />;
default:
return <TextNote key={event.id} event={event} />;
}
};
return (
<div className="mx-auto flex w-full max-w-xl flex-1 flex-col">
<div className="flex-1">
{isLoading || isRefetching ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<LoaderIcon className="size-5 animate-spin" />
</div>
) : (
<Virtualizer overscan={3}>
{data.map((item) => renderItem(item))}
</Virtualizer>
)}
<div className="flex h-20 items-center justify-center">
{hasNextPage ? (
<button
type="button"
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />
Load more
</>
)}
</button>
) : null}
</div>
</div>
</div>
);
}

@ -0,0 +1,108 @@
import { RepostNote } from "@/components/repost";
import { Suggest } from "@/components/suggest";
import { TextNote } from "@/components/text";
import { useArk } from "@lume/ark";
import { LoaderIcon, ArrowRightCircleIcon, InfoIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { FETCH_LIMIT } from "@lume/utils";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { createLazyFileRoute } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
export const Route = createLazyFileRoute("/$account/home/local")({
component: Screen,
});
function Screen() {
const ark = useArk();
const { account } = Route.useParams();
const {
data,
hasNextPage,
isLoading,
isRefetching,
isFetchingNextPage,
fetchNextPage,
} = useInfiniteQuery({
queryKey: ["local_newsfeed", account],
initialPageParam: 0,
queryFn: async ({ pageParam }: { pageParam: number }) => {
const events = await ark.get_events(
"local",
FETCH_LIMIT,
pageParam,
true,
);
return events;
},
getNextPageParam: (lastPage) => {
const lastEvent = lastPage?.at(-1);
if (!lastEvent) return;
return lastEvent.created_at - 1;
},
select: (data) => data?.pages.flatMap((page) => page),
refetchOnWindowFocus: false,
});
const renderItem = (event: Event) => {
if (!event) return;
switch (event.kind) {
case Kind.Repost:
return <RepostNote key={event.id} event={event} />;
default:
return <TextNote key={event.id} event={event} />;
}
};
return (
<div className="mx-auto flex w-full max-w-xl flex-1 flex-col">
<div className="flex-1">
{isLoading || isRefetching ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<LoaderIcon className="size-5 animate-spin" />
</div>
) : !data.length ? (
<div className="flex flex-col gap-3">
<div className="flex items-center gap-2 rounded-xl bg-neutral-50 p-5 dark:bg-neutral-950">
<InfoIcon className="size-6" />
<p>
Empty newsfeed. Or you view the{" "}
<Link
to="/$account/home/global"
className="text-blue-500 hover:text-blue-600"
>
Global Newsfeed
</Link>
</p>
</div>
<Suggest />
</div>
) : (
<Virtualizer overscan={3}>
{data.map((item) => renderItem(item))}
</Virtualizer>
)}
<div className="flex h-20 items-center justify-center">
{hasNextPage ? (
<button
type="button"
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />
Load more
</>
)}
</button>
) : null}
</div>
</div>
</div>
);
}

@ -26,7 +26,7 @@ function Create() {
try {
await ark.save_account(keys);
navigate({
to: "/$account/home",
to: "/$account/home/local",
params: { account: keys.npub },
search: { onboarding: true },
replace: true,

@ -32,7 +32,7 @@ function Import() {
nsec: key,
});
navigate({
to: "/$account/home",
to: "/$account/home/local",
params: { account: npub },
search: { onboarding: true },
replace: true,

@ -15,7 +15,7 @@ export const Route = createFileRoute("/")({
case 0:
const guest = await ark.create_guest_account();
throw redirect({
to: "/$account/home",
to: "/$account/home/local",
params: { account: guest },
search: { guest: true },
replace: true,
@ -26,7 +26,7 @@ export const Route = createFileRoute("/")({
const loadAccount = await ark.load_selected_account(account);
if (loadAccount) {
throw redirect({
to: "/$account/home",
to: "/$account/home/local",
params: { account },
replace: true,
});
@ -50,7 +50,7 @@ function Screen() {
const loadAccount = await ark.load_selected_account(npub);
if (loadAccount) {
navigate({
to: "/$account/home",
to: "/$account/home/local",
params: { account: npub },
replace: true,
});

@ -1,9 +1,8 @@
import { TextNote } from "@/components/text";
import { RepostNote } from "@/components/repost";
import { useArk } from "@lume/ark";
import { ArrowRightCircleIcon, LoaderIcon } from "@lume/icons";
import { ArrowRightCircleIcon, InfoIcon, LoaderIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { EmptyFeed } from "@lume/ui";
import { FETCH_LIMIT } from "@lume/utils";
import { useInfiniteQuery } from "@tanstack/react-query";
@ -43,7 +42,10 @@ export function EventList({ id }: { id: string }) {
<LoaderIcon className="size-5 animate-spin" />
</div>
) : !data.length ? (
<EmptyFeed />
<div className="flex items-center gap-2 rounded-xl bg-neutral-50 p-5 dark:bg-neutral-950">
<InfoIcon className="size-6" />
<p>Empty newsfeed.</p>
</div>
) : (
data.map((item) => renderItem(item))
)}

@ -116,3 +116,7 @@ export * from "./src/arrowUp";
export * from "./src/arrowUpSquare";
export * from "./src/arrowDown";
export * from "./src/link";
export * from "./src/local";
export * from "./src/global";
export * from "./src/infoCircle";
export * from "./src/cancelCircle";

@ -0,0 +1,12 @@
export function CancelCircleIcon(props: JSX.IntrinsicElements["svg"]) {
return (
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
fillRule="evenodd"
d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm7.707-3.707a1 1 0 0 0-1.414 1.414L10.586 12l-2.293 2.293a1 1 0 1 0 1.414 1.414L12 13.414l2.293 2.293a1 1 0 0 0 1.414-1.414L13.414 12l2.293-2.293a1 1 0 0 0-1.414-1.414L12 10.586 9.707 8.293Z"
clipRule="evenodd"
/>
</svg>
);
}

@ -1,22 +1,15 @@
import { SVGProps } from 'react';
import { SVGProps } from "react";
export function CheckCircleIcon(
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) {
return (
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" {...props}>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM15.5805 9.97493C15.8428 9.65434 15.7955 9.18183 15.4749 8.91953C15.1543 8.65724 14.6818 8.70449 14.4195 9.02507L10.4443 13.8837L9.03033 12.4697C8.73744 12.1768 8.26256 12.1768 7.96967 12.4697C7.67678 12.7626 7.67678 13.2374 7.96967 13.5303L9.96967 15.5303C10.1195 15.6802 10.3257 15.7596 10.5374 15.7491C10.749 15.7385 10.9463 15.6389 11.0805 15.4749L15.5805 9.97493Z"
fill="currentColor"
fillRule="evenodd"
d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm3.774 8.133a1 1 0 0 0-1.548-1.266l-3.8 4.645-1.219-1.22a1 1 0 0 0-1.414 1.415l2 2a1 1 0 0 0 1.481-.074l4.5-5.5Z"
clipRule="evenodd"
/>
</svg>
);

@ -0,0 +1,14 @@
export function GlobalIcon(props: JSX.IntrinsicElements["svg"]) {
return (
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
d="M12 2a1 1 0 0 0 0 2 8 8 0 0 1 8 8 1 1 0 0 0 2 0c0-5.523-4.477-10-10-10Z"
/>
<path
fill="currentColor"
d="M12 5a1 1 0 0 0 0 2 5 5 0 0 1 5 5 1 1 0 0 0 2 0 7 7 0 0 0-7-7ZM7.39 6.977a2.9 2.9 0 0 0-2.258-.857c-.83.064-1.632.52-2.065 1.38-1.89 3.749-1.27 8.44 1.862 11.571 3.132 3.132 7.822 3.751 11.57 1.862a2.494 2.494 0 0 0 1.38-2.066 2.9 2.9 0 0 0-.856-2.258L12.914 12.5l.793-.793a1 1 0 0 0-1.414-1.414l-.793.793-4.11-4.11Z"
/>
</svg>
);
}

@ -0,0 +1,16 @@
import { SVGProps } from "react";
export function InfoCircleIcon(
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
) {
return (
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
fillRule="evenodd"
d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm-2 9a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0v-4a1 1 0 0 1-1-1Zm2-4a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"
clipRule="evenodd"
/>
</svg>
);
}

@ -0,0 +1,14 @@
export function LocalIcon(props: JSX.IntrinsicElements["svg"]) {
return (
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
d="M11.19 2.413a.996.996 0 0 0-.19.603V17a1 1 0 1 0 2 0v-6.426l5.504-3.21a1 1 0 0 0 0-1.728l-5.987-3.492a.995.995 0 0 0-1.007-.016.993.993 0 0 0-.32.285Z"
/>
<path
fill="currentColor"
d="M8.19 15.346a1 1 0 1 0-.38-1.964c-1.552.3-2.928.773-3.945 1.398C2.89 15.38 2 16.282 2 17.5c0 .858.45 1.566 1.03 2.099.58.532 1.361.965 2.244 1.308C7.044 21.596 9.423 22 12 22s4.956-.404 6.726-1.093c.883-.343 1.665-.776 2.244-1.308.58-.533 1.03-1.241 1.03-2.099 0-1.218-.89-2.12-1.865-2.72-1.017-.625-2.393-1.098-3.945-1.398a1 1 0 1 0-.38 1.964c1.412.273 2.535.681 3.278 1.138.784.482.912.86.912 1.016 0 .11-.053.322-.384.627-.332.305-.868.626-1.614.916-1.487.578-3.608.957-6.002.957-2.394 0-4.515-.379-6.002-.957-.746-.29-1.282-.611-1.614-.916C4.053 17.822 4 17.609 4 17.5c0-.155.128-.534.912-1.016.743-.457 1.866-.865 3.278-1.138Z"
/>
</svg>
);
}

@ -13,7 +13,7 @@ export function Box({
<div className="h-full w-full flex-1 px-2 pb-2">
<div
className={cn(
"h-full w-full overflow-hidden overflow-y-auto rounded-xl bg-white shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:bg-black dark:shadow-none dark:ring-1 dark:ring-white/5",
"h-full w-full overflow-y-auto rounded-xl bg-white shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:bg-black dark:shadow-none dark:ring-1 dark:ring-white/5",
className,
)}
>

@ -1,34 +0,0 @@
import { InfoIcon } from "@lume/icons";
import { cn } from "@lume/utils";
import { useTranslation } from "react-i18next";
export function EmptyFeed({
text,
subtext,
className,
}: {
text?: string;
subtext?: string;
className?: string;
}) {
const { t } = useTranslation();
return (
<div
className={cn(
"flex w-full flex-col items-center justify-center gap-2 rounded-xl bg-neutral-50 py-3 dark:bg-neutral-950",
className,
)}
>
<InfoIcon className="size-8 text-blue-500" />
<div className="text-center">
<p className="text-lg font-semibold">
{text ? text : t("global.emptyFeedTitle")}
</p>
<p className="text-sm leading-tight">
{subtext ? subtext : t("global.emptyFeedSubtitle")}
</p>
</div>
</div>
);
}

@ -1,7 +1,6 @@
export * from "./user";
export * from "./note";
export * from "./column";
export * from "./emptyFeed";
// UI
export * from "./container";

@ -5,7 +5,6 @@ import {
NOSTR_EVENTS,
NOSTR_MENTIONS,
VIDEOS,
canPreview,
cn,
} from "@lume/utils";
import { NIP89 } from "./nip89";
@ -18,7 +17,6 @@ import { MentionNote } from "./mentions/note";
import { Hashtag } from "./mentions/hashtag";
import { VideoPreview } from "./preview/video";
import { stripHtml } from "string-strip-html";
import getUrl from "get-urls";
import { ImagePreview } from "./preview/image";
export function NoteContent({ className }: { className?: string }) {
@ -26,7 +24,6 @@ export function NoteContent({ className }: { className?: string }) {
const content = useMemo(() => {
const text = stripHtml(event.content.trim()).result;
const words = text.split(/( |\n)/);
const urls = [...getUrl(text)];
// @ts-ignore, kaboom !!!
let parsedContent: ReactNode[] = text;

@ -4,6 +4,7 @@ import { writeText } from "@tauri-apps/plugin-clipboard-manager";
import { useTranslation } from "react-i18next";
import { useNoteContext } from "./provider";
import { useArk } from "@lume/ark";
import { toast } from "sonner";
export function NoteMenu() {
const ark = useArk();
@ -13,20 +14,24 @@ export function NoteMenu() {
const copyID = async () => {
await writeText(await ark.event_to_bech32(event.id, [""]));
toast.success("Copied");
};
const copyRaw = async () => {
await writeText(JSON.stringify(event));
toast.success("Copied");
};
const copyNpub = async () => {
await writeText(await ark.user_to_bech32(event.pubkey, [""]));
toast.success("Copied");
};
const copyLink = async () => {
await writeText(
`https://njump.me/${await ark.event_to_bech32(event.id, [""])}`,
);
toast.success("Copied");
};
return (

@ -37,7 +37,7 @@ export function ImagePreview({ url }: { url: string }) {
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<div
onClick={open}
className="group relative my-1 overflow-hidden rounded-xl"
className="group relative my-1 overflow-hidden rounded-xl ring-1 ring-neutral-100 dark:ring-neutral-900"
>
<img
src={url}

@ -21,7 +21,7 @@ export function VideoPreview({ url }: { url: string }) {
<MediaControlBar>
<MediaPlayButton />
<MediaTimeRange />
<MediaTimeDisplay showDuration />
<MediaTimeDisplay />
<MediaMuteButton />
</MediaControlBar>
</MediaController>

@ -2,18 +2,15 @@ import { cn } from "@lume/utils";
import { ReactNode } from "react";
export function NoteRoot({
children,
className,
children,
className,
}: {
children: ReactNode;
className?: string;
children: ReactNode;
className?: string;
}) {
return (
<div
className={cn("h-min w-full overflow-hidden", className)}
contentEditable={false}
>
{children}
</div>
);
return (
<div className={cn("h-min w-full", className)} contentEditable={false}>
{children}
</div>
);
}

406
pnpm-lock.yaml generated

@ -874,19 +874,19 @@ importers:
version: 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collapsible':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dialog':
specifier: ^1.0.5
version: 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dropdown-menu':
specifier: ^2.0.6
version: 2.0.6(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
version: 2.0.6(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-hover-card':
specifier: ^1.0.7
version: 1.0.7(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popover':
specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.7(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-tooltip':
specifier: ^1.0.7
version: 1.0.7(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
@ -1885,13 +1885,13 @@ packages:
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collapsible': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
@ -1915,8 +1915,8 @@ packages:
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dialog': 1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
@ -1944,6 +1944,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-arrow@1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-avatar@1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
peerDependencies:
@ -1959,7 +1979,7 @@ packages:
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
@ -2023,6 +2043,33 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collapsible@1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies:
@ -2047,6 +2094,29 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collection@1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.61)(react@18.2.0):
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies:
@ -2109,6 +2179,39 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.61)(react@18.2.0)
dev: false
/@radix-ui/react-dialog@1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.61)(react@18.2.0)
dev: false
/@radix-ui/react-direction@1.0.1(@types/react@18.2.61)(react@18.2.0):
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
peerDependencies:
@ -2148,6 +2251,30 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-dismissable-layer@1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
peerDependencies:
@ -2175,6 +2302,32 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-dropdown-menu@2.0.6(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-menu': 2.0.6(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.61)(react@18.2.0):
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
peerDependencies:
@ -2212,6 +2365,28 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-focus-scope@1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-hover-card@1.0.7(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==}
peerDependencies:
@ -2229,11 +2404,11 @@ packages:
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
@ -2293,6 +2468,43 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.61)(react@18.2.0)
dev: false
/@radix-ui/react-menu@2.0.6(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.61)(react@18.2.0)
dev: false
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
peerDependencies:
@ -2328,6 +2540,40 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.61)(react@18.2.0)
dev: false
/@radix-ui/react-popover@1.0.7(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.61)(react@18.2.0)
dev: false
/@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
peerDependencies:
@ -2358,6 +2604,35 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-popper@1.1.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
peerDependencies:
@ -2379,6 +2654,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-portal@1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
peerDependencies:
@ -2401,6 +2696,27 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-presence@1.0.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
@ -2422,6 +2738,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies:
@ -2451,6 +2787,34 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-roving-focus@1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-slot@1.0.2(@types/react@18.2.61)(react@18.2.0):
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
@ -2483,12 +2847,12 @@ packages:
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
@ -2613,7 +2977,7 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.24.0
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.61)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)

@ -22,7 +22,6 @@ fn main() {
.setup(|app| {
let _tray = tray::create_tray(app.handle()).unwrap();
let handle = app.handle().clone();
let resource_dir = handle.path().resource_dir().unwrap();
let home_dir = handle.path().home_dir().unwrap();
// create data folder if not exist
@ -30,12 +29,13 @@ fn main() {
tauri::async_runtime::spawn(async move {
// Create nostr database connection
let nostr_db = SQLiteDatabase::open(resource_dir.join("lume.db"))
.await
.expect("Open database failed.");
let sqlite = SQLiteDatabase::open(home_dir.join("Lume/lume.db")).await;
// Create nostr connection
let client = ClientBuilder::default().database(nostr_db).build();
let client = match sqlite {
Ok(db) => ClientBuilder::default().database(db).build(),
Err(_) => ClientBuilder::default().build(),
};
// Add some bootstrap relays
// #TODO: Pull bootstrap relays from user's settings