mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
feat: polish
This commit is contained in:
parent
ab27bd5f44
commit
f908c46a19
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
@ -50,7 +50,6 @@ export function ActivityList() {
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
staleTime: 360 * 1000,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
|
@ -145,10 +145,11 @@ export function CreateAccountScreen() {
|
||||
|
||||
// add account to storage
|
||||
await storage.createSetting("nsecbunker", "1");
|
||||
await storage.createAccount({
|
||||
const dbAccount = await storage.createAccount({
|
||||
pubkey: account,
|
||||
privkey: localSigner.privateKey,
|
||||
});
|
||||
ark.account = dbAccount;
|
||||
|
||||
// get final signer with newly created account
|
||||
const finalSigner = new NDKNip46Signer(bunker, account, localSigner);
|
||||
@ -156,7 +157,6 @@ export function CreateAccountScreen() {
|
||||
|
||||
// update main ndk instance signer
|
||||
ark.updateNostrSigner({ signer: finalSigner });
|
||||
console.log(ark.ndk.signer);
|
||||
|
||||
// remove default nsecbunker profile and contact list
|
||||
await ark.createEvent({ kind: NDKKind.Metadata, content: "", tags: [] });
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { getPublicKey, nip19 } from "nostr-tools";
|
||||
@ -7,6 +8,7 @@ import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export function LoginWithKey() {
|
||||
const ark = useArk();
|
||||
const storage = useStorage();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -30,10 +32,11 @@ export function LoginWithKey() {
|
||||
const privkey = nip19.decode(data.nsec).data as string;
|
||||
const pubkey = getPublicKey(privkey);
|
||||
|
||||
await storage.createAccount({
|
||||
const account = await storage.createAccount({
|
||||
pubkey: pubkey,
|
||||
privkey: privkey,
|
||||
});
|
||||
ark.account = account;
|
||||
|
||||
return navigate("/auth/onboarding", { replace: true });
|
||||
} catch (e) {
|
||||
|
@ -48,10 +48,11 @@ export function LoginWithNsecbunker() {
|
||||
ark.updateNostrSigner({ signer: remoteSigner });
|
||||
|
||||
await storage.createSetting("nsecbunker", "1");
|
||||
await storage.createAccount({
|
||||
pubkey,
|
||||
const account = await storage.createAccount({
|
||||
pubkey: pubkey,
|
||||
privkey: localSigner.privateKey,
|
||||
});
|
||||
ark.account = account;
|
||||
|
||||
return navigate("/auth/onboarding", { replace: true });
|
||||
} catch (e) {
|
||||
|
@ -109,10 +109,11 @@ export function LoginWithOAuth() {
|
||||
ark.updateNostrSigner({ signer: remoteSigner });
|
||||
|
||||
await storage.createSetting("nsecbunker", "1");
|
||||
await storage.createAccount({
|
||||
const account = await storage.createAccount({
|
||||
pubkey,
|
||||
privkey: localSigner.privateKey,
|
||||
});
|
||||
ark.account = account;
|
||||
|
||||
return navigate("/auth/onboarding", { replace: true });
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export function OnboardingScreen() {
|
||||
|
||||
const toggleLowPower = async () => {
|
||||
await storage.createSetting("lowPower", String(+!settings.lowPower));
|
||||
setSettings((state) => ({ ...state, autoupdate: !settings.lowPower }));
|
||||
setSettings((state) => ({ ...state, lowPower: !settings.lowPower }));
|
||||
};
|
||||
|
||||
const toggleTranslation = async () => {
|
||||
@ -58,7 +58,7 @@ export function OnboardingScreen() {
|
||||
setLoading(true);
|
||||
|
||||
// get account contacts
|
||||
await ark.getUserContacts(ark.account.pubkey);
|
||||
await ark.getUserContacts();
|
||||
|
||||
// refetch newsfeed
|
||||
await queryClient.prefetchInfiniteQuery({
|
||||
@ -154,7 +154,7 @@ export function OnboardingScreen() {
|
||||
<h3 className="font-semibold text-lg">Low Power Mode</h3>
|
||||
<p className="text-neutral-500">
|
||||
Limited relay connection and hide all media, sustainable for low
|
||||
network environment
|
||||
network environment.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -171,7 +171,8 @@ export function OnboardingScreen() {
|
||||
Translation (nostr.wine)
|
||||
</h3>
|
||||
<p className="text-neutral-500">
|
||||
Translate text to your preferred language, powered by Nostr Wine
|
||||
Translate text to your preferred language, powered by Nostr
|
||||
Wine.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -87,7 +87,7 @@ export function DepotMembers() {
|
||||
</div>
|
||||
</div>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-sm dark:bg-black/20" />
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-black/10" />
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||
<div className="relative h-min w-full max-w-xl overflow-hidden rounded-xl bg-white dark:bg-black">
|
||||
<div className="inline-flex h-14 w-full shrink-0 items-center justify-between border-b border-neutral-100 px-5 dark:border-neutral-900">
|
||||
|
@ -13,14 +13,14 @@ export function AdvancedSettingScreen() {
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||
Caches
|
||||
Cache
|
||||
</div>
|
||||
<div className="text-sm">Use for boost up NDK</div>
|
||||
<div className="text-sm">Use for boost up nostr connection</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => clearCache()}
|
||||
className="h-8 w-max rounded-lg bg-blue-500 px-3 text-sm font-medium text-white hover:bg-blue-600"
|
||||
className="h-8 w-max rounded-lg px-3 text-sm font-semibold text-blue-500 bg-blue-100 hover:bg-blue-200"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { compactNumber } from "@lume/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function PostCard() {
|
||||
const storage = useStorage();
|
||||
const ark = useArk();
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ["user-stats", ark.account.pubkey],
|
||||
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useProfile } from "@lume/ark";
|
||||
import { useArk, useProfile } from "@lume/ark";
|
||||
import { EditIcon, LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { displayNpub } from "@lume/utils";
|
||||
import * as Avatar from "@radix-ui/react-avatar";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
@ -9,7 +8,7 @@ import { nip19 } from "nostr-tools";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function ProfileCard() {
|
||||
const storage = useStorage();
|
||||
const ark = useArk();
|
||||
const svgURI = `data:image/svg+xml;utf8,${encodeURIComponent(
|
||||
minidenticon(ark.account.pubkey, 90, 50),
|
||||
)}`;
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { EditIcon, LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { compactNumber } from "@lume/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export function RelayCard() {
|
||||
const ark = useArk();
|
||||
const storage = useStorage();
|
||||
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ["relays", ark.account.pubkey],
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { compactNumber } from "@lume/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
|
||||
export function ZapCard() {
|
||||
const storage = useStorage();
|
||||
const ark = useArk();
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ["user-stats", ark.account.pubkey],
|
||||
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||
|
@ -15,6 +15,7 @@ export function GeneralSettingScreen() {
|
||||
const storage = useStorage();
|
||||
|
||||
const [settings, setSettings] = useState({
|
||||
lowPower: false,
|
||||
autoupdate: false,
|
||||
autolaunch: false,
|
||||
outbox: false,
|
||||
@ -30,6 +31,11 @@ export function GeneralSettingScreen() {
|
||||
setSettings((prev) => ({ ...prev, appearance: theme }));
|
||||
};
|
||||
|
||||
const toggleLowPower = async () => {
|
||||
await storage.createSetting("lowPower", String(+!settings.lowPower));
|
||||
setSettings((state) => ({ ...state, lowPower: !settings.lowPower }));
|
||||
};
|
||||
|
||||
const toggleAutolaunch = async () => {
|
||||
if (!settings.autolaunch) {
|
||||
await enable();
|
||||
@ -42,12 +48,6 @@ export function GeneralSettingScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
const toggleOutbox = async () => {
|
||||
await storage.createSetting("outbox", String(+!settings.outbox));
|
||||
// update state
|
||||
setSettings((prev) => ({ ...prev, outbox: !settings.outbox }));
|
||||
};
|
||||
|
||||
const toggleMedia = async () => {
|
||||
await storage.createSetting("media", String(+!settings.media));
|
||||
storage.settings.media = !settings.media;
|
||||
@ -98,6 +98,12 @@ export function GeneralSettingScreen() {
|
||||
autoupdate: !!parseInt(item.value),
|
||||
}));
|
||||
|
||||
if (item.key === "lowPower")
|
||||
setSettings((prev) => ({
|
||||
...prev,
|
||||
lowPower: !!parseInt(item.value),
|
||||
}));
|
||||
|
||||
if (item.key === "outbox")
|
||||
setSettings((prev) => ({
|
||||
...prev,
|
||||
@ -127,9 +133,9 @@ export function GeneralSettingScreen() {
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||
Updater
|
||||
Update
|
||||
</div>
|
||||
<div className="text-sm">Auto download new update at Login</div>
|
||||
<div className="text-sm">Automatically download new update</div>
|
||||
</div>
|
||||
<Switch.Root
|
||||
checked={settings.autoupdate}
|
||||
@ -139,6 +145,23 @@ export function GeneralSettingScreen() {
|
||||
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||
</Switch.Root>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||
Low Power
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
Sustainable for low network environment.
|
||||
</div>
|
||||
</div>
|
||||
<Switch.Root
|
||||
checked={settings.lowPower}
|
||||
onClick={() => toggleLowPower()}
|
||||
className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
|
||||
>
|
||||
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||
</Switch.Root>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||
@ -154,21 +177,6 @@ export function GeneralSettingScreen() {
|
||||
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||
</Switch.Root>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||
Gossip
|
||||
</div>
|
||||
<div className="text-sm">Use Outbox model</div>
|
||||
</div>
|
||||
<Switch.Root
|
||||
checked={settings.outbox}
|
||||
onClick={() => toggleOutbox()}
|
||||
className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
|
||||
>
|
||||
<Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
|
||||
</Switch.Root>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||
@ -189,7 +197,7 @@ export function GeneralSettingScreen() {
|
||||
<div className="w-24 shrink-0 text-end text-sm font-semibold">
|
||||
Hashtag
|
||||
</div>
|
||||
<div className="text-sm">Hide all hashtags in content</div>
|
||||
<div className="text-sm">Show all hashtags in content</div>
|
||||
</div>
|
||||
<Switch.Root
|
||||
checked={settings.hashtag}
|
||||
|
@ -114,7 +114,7 @@ export function NoteZap() {
|
||||
</button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-xl dark:bg-white/20" />
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
|
||||
<div className="relative w-full max-w-xl bg-white h-min rounded-xl dark:bg-black">
|
||||
<div className="inline-flex items-center justify-between w-full px-5 py-3 shrink-0">
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
NOSTR_EVENTS,
|
||||
NOSTR_MENTIONS,
|
||||
VIDEOS,
|
||||
canPreview,
|
||||
cn,
|
||||
regionNames,
|
||||
} from "@lume/utils";
|
||||
@ -27,9 +28,11 @@ import { useNoteContext } from "./provider";
|
||||
|
||||
export function NoteContent({
|
||||
className,
|
||||
mini = false,
|
||||
isTranslatable = false,
|
||||
}: {
|
||||
className?: string;
|
||||
mini?: boolean;
|
||||
isTranslatable?: boolean;
|
||||
}) {
|
||||
const storage = useStorage();
|
||||
@ -52,7 +55,7 @@ export function NoteContent({
|
||||
const words = text.split(/( |\n)/);
|
||||
const urls = [...getUrls(text)];
|
||||
|
||||
if (storage.settings.media && !storage.settings.lowPower) {
|
||||
if (storage.settings.media && !storage.settings.lowPower && !mini) {
|
||||
images = urls.filter((word) =>
|
||||
IMAGES.some((el) => {
|
||||
const url = new URL(word);
|
||||
@ -79,9 +82,11 @@ export function NoteContent({
|
||||
);
|
||||
}
|
||||
|
||||
events = words.filter((word) =>
|
||||
NOSTR_EVENTS.some((el) => word.startsWith(el)),
|
||||
);
|
||||
if (!mini) {
|
||||
events = words.filter((word) =>
|
||||
NOSTR_EVENTS.some((el) => word.startsWith(el)),
|
||||
);
|
||||
}
|
||||
|
||||
const hashtags = words.filter((word) => word.startsWith("#"));
|
||||
const mentions = words.filter((word) =>
|
||||
@ -198,7 +203,7 @@ export function NoteContent({
|
||||
(match, i) => {
|
||||
const url = new URL(match);
|
||||
|
||||
if (!linkPreview) {
|
||||
if (!linkPreview && canPreview(match)) {
|
||||
linkPreview = match;
|
||||
return <LinkPreview key={match + i} url={url.toString()} />;
|
||||
}
|
||||
@ -217,9 +222,11 @@ export function NoteContent({
|
||||
},
|
||||
);
|
||||
|
||||
parsedContent = reactStringReplace(parsedContent, "\n", () => {
|
||||
return <div key={nanoid()} className="h-3" />;
|
||||
});
|
||||
if (!mini) {
|
||||
parsedContent = reactStringReplace(parsedContent, "\n", () => {
|
||||
return <div key={nanoid()} className="h-3" />;
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof parsedContent[0] === "string") {
|
||||
parsedContent[0] = parsedContent[0].trimStart();
|
||||
@ -259,7 +266,12 @@ export function NoteContent({
|
||||
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
<div className="break-p select-text whitespace-pre-line text-balance leading-normal">
|
||||
<div
|
||||
className={cn(
|
||||
"break-p select-text text-balance leading-normal",
|
||||
!mini ? "whitespace-pre-line" : "",
|
||||
)}
|
||||
>
|
||||
{richContent}
|
||||
</div>
|
||||
{isTranslatable && storage.settings.translation ? (
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { PinIcon } from "@lume/icons";
|
||||
import { COL_TYPES } from "@lume/utils";
|
||||
import { memo } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Note } from "../";
|
||||
import { useEvent } from "../../../hooks/useEvent";
|
||||
import { useColumnContext } from "../../column/provider";
|
||||
import { User } from "../../user";
|
||||
|
||||
export const MentionNote = memo(function MentionNote({
|
||||
eventId,
|
||||
openable = true,
|
||||
}: { eventId: string; openable?: boolean }) {
|
||||
const { addColumn } = useColumnContext();
|
||||
const { isLoading, isError, data } = useEvent(eventId);
|
||||
|
||||
if (isLoading) {
|
||||
@ -34,11 +38,11 @@ export const MentionNote = memo(function MentionNote({
|
||||
|
||||
return (
|
||||
<Note.Provider event={data}>
|
||||
<Note.Root className="flex flex-col w-full gap-1 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800">
|
||||
<Note.Root className="flex flex-col w-full my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-100 dark:border-neutral-900">
|
||||
<User.Provider pubkey={data.pubkey}>
|
||||
<User.Root className="px-3 mt-3 flex h-6 items-center gap-2">
|
||||
<User.Root className="flex h-10 px-3 items-center gap-2">
|
||||
<User.Avatar className="size-6 shrink-0 rounded-md object-cover" />
|
||||
<div className="flex flex-1 items-baseline gap-2">
|
||||
<div className="flex-1 inline-flex gap-2">
|
||||
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
|
||||
<span className="text-neutral-600 dark:text-neutral-400">·</span>
|
||||
<User.Time
|
||||
@ -48,17 +52,30 @@ export const MentionNote = memo(function MentionNote({
|
||||
</div>
|
||||
</User.Root>
|
||||
</User.Provider>
|
||||
<div className="px-3 pb-3 mt-1">
|
||||
<Note.Content />
|
||||
{openable ? (
|
||||
<Note.Content mini className="px-3" />
|
||||
{openable ? (
|
||||
<div className="mt-2 px-3 flex items-center justify-between">
|
||||
<Link
|
||||
to={`/events/${data.id}`}
|
||||
className="mt-2 text-blue-500 hover:text-blue-600"
|
||||
className="text-sm font-medium text-blue-500 hover:text-blue-600"
|
||||
>
|
||||
Show more
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () =>
|
||||
await addColumn({
|
||||
kind: COL_TYPES.thread,
|
||||
title: "Thread",
|
||||
content: data.id,
|
||||
})
|
||||
}
|
||||
className="inline-flex items-center justify-center rounded-md text-neutral-600 dark:text-neutral-400 size-6 bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
|
||||
>
|
||||
<PinIcon className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</Note.Root>
|
||||
</Note.Provider>
|
||||
);
|
||||
|
@ -12,7 +12,7 @@ export function NoteUser({
|
||||
return (
|
||||
<User.Provider pubkey={event.pubkey}>
|
||||
<User.Root className={cn("flex items-center gap-3", className)}>
|
||||
<User.Avatar className="size-9 shrink-0 rounded-lg bg-white object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
|
||||
<User.Avatar className="size-9 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
|
||||
<div className="flex h-6 flex-1 items-start justify-between gap-2">
|
||||
<User.Name className="font-semibold text-neutral-950 dark:text-neutral-50" />
|
||||
<User.Time
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { cn } from "@lume/utils";
|
||||
import * as Avatar from "@radix-ui/react-avatar";
|
||||
import { minidenticon } from "minidenticons";
|
||||
@ -7,6 +8,8 @@ import { useUserContext } from "./provider";
|
||||
|
||||
export function UserAvatar({ className }: { className?: string }) {
|
||||
const user = useUserContext();
|
||||
const storage = useStorage();
|
||||
|
||||
const fallbackAvatar = useMemo(
|
||||
() =>
|
||||
`data:image/svg+xml;utf8,${encodeURIComponent(
|
||||
@ -20,7 +23,7 @@ export function UserAvatar({ className }: { className?: string }) {
|
||||
<div className="shrink-0">
|
||||
<div
|
||||
className={cn(
|
||||
"bg-black/20 dark:bg-white/20 animate-pulse",
|
||||
"bg-black/20 dark:bg-white/20 rounded animate-pulse",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
@ -30,13 +33,23 @@ export function UserAvatar({ className }: { className?: string }) {
|
||||
|
||||
return (
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user.image}
|
||||
alt={user.pubkey}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
className={className}
|
||||
/>
|
||||
{storage.settings.lowPower ? (
|
||||
<Avatar.Image
|
||||
src={fallbackAvatar}
|
||||
alt={user.pubkey}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
className={cn("bg-black dark:bg-white", className)}
|
||||
/>
|
||||
) : (
|
||||
<Avatar.Image
|
||||
src={user.image}
|
||||
alt={user.pubkey}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
<Avatar.Fallback delayMs={120}>
|
||||
<img
|
||||
src={fallbackAvatar}
|
||||
|
@ -8,7 +8,7 @@ export function UserName({ className }: { className?: string }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"h-4 w-20 bg-black/20 dark:bg-white/20 animate-pulse",
|
||||
"h-4 w-20 bg-black/20 dark:bg-white/20 rounded animate-pulse",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
@ -16,7 +16,7 @@ export function UserName({ className }: { className?: string }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("w-full max-w-[15rem] truncate", className)}>
|
||||
<div className={cn("truncate", className)}>
|
||||
{user.displayName || user.name || "Anon"}
|
||||
</div>
|
||||
);
|
||||
|
@ -29,7 +29,7 @@ export function UserNip05({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"h-4 w-20 bg-black/20 dark:bg-white/20 animate-pulse",
|
||||
"h-4 w-20 bg-black/20 dark:bg-white/20 rounded animate-pulse",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
|
@ -9,6 +9,7 @@ import NDK, {
|
||||
NDKPrivateKeySigner,
|
||||
NDKRelay,
|
||||
NDKRelayAuthPolicies,
|
||||
NDKUser,
|
||||
} from "@nostr-dev-kit/ndk";
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
import Linkify from "linkify-react";
|
||||
@ -19,7 +20,9 @@ import { LumeContext } from "./context";
|
||||
|
||||
export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
const storage = useStorage();
|
||||
const [context, setContext] = useState<Ark>(undefined);
|
||||
|
||||
const [ark, setArk] = useState<Ark>(undefined);
|
||||
const [ndk, setNDK] = useState<NDK>(undefined);
|
||||
|
||||
async function initNostrSigner({
|
||||
nsecbunker,
|
||||
@ -67,7 +70,7 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
async function initNDK() {
|
||||
const explicitRelayUrls = normalizeRelayUrlSet([
|
||||
"wss://bostr.nokotaro.com/",
|
||||
"wss://nostr.mutinywallet.com/",
|
||||
@ -77,14 +80,12 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
const outboxRelayUrls = normalizeRelayUrlSet(["wss://purplepag.es/"]);
|
||||
|
||||
// #TODO: user should config blacklist relays
|
||||
// No need to connect depot tunnel url
|
||||
const blacklistRelayUrls = storage.settings.tunnelUrl.length
|
||||
? [
|
||||
storage.settings.tunnelUrl,
|
||||
`${storage.settings.tunnelUrl}/`,
|
||||
"wss://brb.io/",
|
||||
]
|
||||
: ["wss://brb.io/"];
|
||||
// Skip connect depot tunnel url
|
||||
const blacklistRelayUrls = normalizeRelayUrlSet(
|
||||
storage.settings.tunnelUrl.length
|
||||
? [storage.settings.tunnelUrl, "wss://brb.io/"]
|
||||
: ["wss://brb.io/"],
|
||||
);
|
||||
|
||||
const cacheAdapter = new NDKCacheAdapterTauri(storage);
|
||||
const ndk = new NDK({
|
||||
@ -115,29 +116,37 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
// auth
|
||||
ndk.relayAuthDefaultPolicy = async (relay: NDKRelay, challenge: string) => {
|
||||
const signIn = NDKRelayAuthPolicies.signIn({ ndk });
|
||||
const event = await signIn(relay, challenge).catch((e) => console.log(e));
|
||||
const event = await signIn(relay, challenge).catch((e) =>
|
||||
console.error(e),
|
||||
);
|
||||
if (event) {
|
||||
sendNativeNotification(
|
||||
await sendNativeNotification(
|
||||
`You've sign in sucessfully to relay: ${relay.url}`,
|
||||
);
|
||||
return event;
|
||||
}
|
||||
};
|
||||
|
||||
// update account's metadata
|
||||
if (signer) {
|
||||
const user = ndk.getUser({ pubkey: storage.currentUser.pubkey });
|
||||
setNDK(ndk);
|
||||
}
|
||||
|
||||
async function initArk() {
|
||||
// ark utils
|
||||
const ark = new Ark({ ndk, account: storage.currentUser });
|
||||
|
||||
if (ndk && storage.currentUser) {
|
||||
const user = new NDKUser({ pubkey: storage.currentUser.pubkey });
|
||||
ndk.activeUser = user;
|
||||
|
||||
const contacts = await user.follows();
|
||||
storage.currentUser.contacts = [...contacts].map((user) => user.pubkey);
|
||||
// update contacts
|
||||
await ark.getUserContacts();
|
||||
|
||||
// subscribe for new activity
|
||||
const sub = ndk.subscribe(
|
||||
{
|
||||
kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Zap],
|
||||
since: Math.floor(Date.now() / 1000),
|
||||
"#p": [storage.currentUser.pubkey],
|
||||
"#p": [ark.account.pubkey],
|
||||
},
|
||||
{ closeOnEose: false, groupable: false },
|
||||
);
|
||||
@ -169,18 +178,18 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
});
|
||||
}
|
||||
|
||||
// ark utils
|
||||
const ark = new Ark({ ndk, account: storage.currentUser });
|
||||
|
||||
// update context
|
||||
setContext(ark);
|
||||
setArk(ark);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!context) init();
|
||||
if (ndk) initArk();
|
||||
}, [ndk]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ark && !ndk) initNDK();
|
||||
}, []);
|
||||
|
||||
if (!context) {
|
||||
if (!ark) {
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
@ -207,7 +216,5 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<LumeContext.Provider value={context}>{children}</LumeContext.Provider>
|
||||
);
|
||||
return <LumeContext.Provider value={ark}>{children}</LumeContext.Provider>;
|
||||
};
|
||||
|
@ -279,9 +279,6 @@ export class LumeStorage {
|
||||
}
|
||||
|
||||
const account = await this.getActiveAccount();
|
||||
this.currentUser = account;
|
||||
this.currentUser.contacts = [];
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
import { activityAtom } from "@lume/utils";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { ActivityContent } from "./content";
|
||||
|
||||
export function Activity() {
|
||||
const isActivityOpen = useAtomValue(activityAtom);
|
||||
|
||||
return (
|
||||
<AnimatePresence initial={false} mode="wait">
|
||||
{isActivityOpen ? (
|
||||
<motion.div
|
||||
key={isActivityOpen ? "activity-open" : "activity-close"}
|
||||
layout
|
||||
initial={{ scale: 0.9, opacity: 0, translateX: -20 }}
|
||||
animate={{
|
||||
scale: [0.95, 1],
|
||||
opacity: [0.5, 1],
|
||||
translateX: [-10, 0],
|
||||
}}
|
||||
exit={{
|
||||
scale: [0.95, 0.9],
|
||||
opacity: [0.5, 0],
|
||||
translateX: [-10, -20],
|
||||
}}
|
||||
className="h-full w-[350px] px-1 pb-1 shrink-0"
|
||||
>
|
||||
<ActivityContent />
|
||||
</motion.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { ReplyActivity } from "./reply";
|
||||
import { RepostActivity } from "./repost";
|
||||
import { ZapActivity } from "./zap";
|
||||
|
||||
export function ActivityContent() {
|
||||
const ark = useArk();
|
||||
const storage = useStorage();
|
||||
|
||||
const { isLoading, data, hasNextPage, isFetchingNextPage, fetchNextPage } =
|
||||
useInfiniteQuery({
|
||||
queryKey: ["activity"],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({
|
||||
signal,
|
||||
pageParam,
|
||||
}: {
|
||||
signal: AbortSignal;
|
||||
pageParam: number;
|
||||
}) => {
|
||||
const events = await ark.getInfiniteEvents({
|
||||
filter: {
|
||||
kinds: [NDKKind.Zap],
|
||||
"#p": [ark.account.pubkey],
|
||||
},
|
||||
limit: 100,
|
||||
pageParam,
|
||||
signal,
|
||||
});
|
||||
|
||||
return events;
|
||||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
const lastEvent = lastPage.at(-1);
|
||||
if (!lastEvent) return;
|
||||
return lastEvent.created_at - 1;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
});
|
||||
|
||||
const allEvents = useMemo(
|
||||
() => (data ? data.pages.flatMap((page) => page) : []),
|
||||
[data],
|
||||
);
|
||||
|
||||
const renderEvent = useCallback((event: NDKEvent) => {
|
||||
if (event.pubkey === ark.account.pubkey) return null;
|
||||
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
return <ReplyActivity key={event.id} event={event} />;
|
||||
case NDKKind.Repost:
|
||||
return <RepostActivity key={event.id} event={event} />;
|
||||
case NDKKind.Zap:
|
||||
return <ZapActivity key={event.id} event={event} />;
|
||||
default:
|
||||
return <ReplyActivity key={event.id} event={event} />;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col justify-between rounded-xl overflow-hidden 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/10">
|
||||
<div className="h-full w-full min-h-0">
|
||||
{isLoading ? (
|
||||
<div className="w-[350px] h-full flex items-center justify-center">
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
</div>
|
||||
) : allEvents.length < 1 ? (
|
||||
<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>
|
||||
) : (
|
||||
renderEvent(allEvents[0])
|
||||
)}
|
||||
</div>
|
||||
<div className="h-16 shrink-0 px-3 flex items-center gap-3 bg-neutral-100 dark:bg-neutral-900">
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 flex-1 inline-flex items-center justify-center rounded-xl font-medium bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 flex-1 inline-flex items-center justify-center rounded-xl font-medium bg-blue-500 text-white hover:bg-blue-600"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
import { Note, useArk } from "@lume/ark";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import { ActivityRootNote } from "./rootNote";
|
||||
|
||||
export function ReplyActivity({ event }: { event: NDKEvent }) {
|
||||
const ark = useArk();
|
||||
const thread = ark.getEventThread({
|
||||
content: event.content,
|
||||
tags: event.tags,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-full pb-3 flex flex-col justify-between">
|
||||
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
|
||||
<h3 className="text-center font-semibold leading-tight">
|
||||
Conversation
|
||||
</h3>
|
||||
<p className="text-sm text-blue-500 font-medium leading-tight">
|
||||
@ Someone has replied to your note
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-3">
|
||||
{thread ? (
|
||||
<div className="flex flex-col gap-3 mb-1">
|
||||
<ActivityRootNote eventId={thread.rootEventId} />
|
||||
<ActivityRootNote eventId={thread.replyEventId} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-3 flex flex-col gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-teal-500 font-medium">New reply</p>
|
||||
<div className="flex-1 h-px bg-teal-300" />
|
||||
<div className="w-4 shrink-0 h-px bg-teal-300" />
|
||||
</div>
|
||||
<Note.Provider event={event}>
|
||||
<Note.Root>
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.User className="flex-1 pr-1" />
|
||||
</div>
|
||||
<Note.Content className="min-w-0 px-3" />
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.Pin />
|
||||
<div className="inline-flex items-center gap-10" />
|
||||
</div>
|
||||
</Note.Root>
|
||||
</Note.Provider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import { formatCreatedAt } from "@lume/utils";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import { User } from "../user";
|
||||
import { ActivityRootNote } from "./rootNote";
|
||||
|
||||
export function RepostActivity({ event }: { event: NDKEvent }) {
|
||||
const repostId = event.tags.find((el) => el[0] === "e")[1];
|
||||
const createdAt = formatCreatedAt(event.created_at);
|
||||
|
||||
return (
|
||||
<div className="h-full pb-3 flex flex-col justify-between">
|
||||
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
|
||||
<h3 className="text-center font-semibold leading-tight">Boost</h3>
|
||||
<p className="text-sm text-blue-500 font-medium leading-tight">
|
||||
@ Someone has reposted to your note
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<User pubkey={event.pubkey} variant="notify2" />
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-teal-500 font-medium">Reposted</p>
|
||||
<div className="flex-1 h-px bg-teal-300" />
|
||||
<div className="w-4 shrink-0 h-px bg-teal-300" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-col gap-3">
|
||||
<ActivityRootNote eventId={repostId} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
import { Note, useEvent } from "@lume/ark";
|
||||
|
||||
export function ActivityRootNote({ eventId }: { eventId: string }) {
|
||||
const { isLoading, isError, data } = useEvent(eventId);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="relative flex gap-3">
|
||||
<div className="relative flex-1 rounded-md bg-neutral-200 px-2 py-2 dark:bg-neutral-800">
|
||||
<div className="h-4 w-full animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="relative flex gap-3">
|
||||
<div className="relative flex-1 rounded-md bg-neutral-200 px-2 py-2 dark:bg-neutral-800">
|
||||
Failed to fetch event
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Note.Provider event={data}>
|
||||
<Note.Root>
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.User className="flex-1 pr-1" />
|
||||
</div>
|
||||
<Note.Content className="min-w-0 px-3" />
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.Pin />
|
||||
<div className="inline-flex items-center gap-10" />
|
||||
</div>
|
||||
</Note.Root>
|
||||
</Note.Provider>
|
||||
);
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import { compactNumber, formatCreatedAt } from "@lume/utils";
|
||||
import { NDKEvent, zapInvoiceFromEvent } from "@nostr-dev-kit/ndk";
|
||||
import { User } from "../user";
|
||||
import { ActivityRootNote } from "./rootNote";
|
||||
|
||||
export function ZapActivity({ event }: { event: NDKEvent }) {
|
||||
const zapEventId = event.tags.find((tag) => tag[0] === "e")[1];
|
||||
const invoice = zapInvoiceFromEvent(event);
|
||||
|
||||
return (
|
||||
<div className="h-full pb-3 flex flex-col justify-between">
|
||||
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
|
||||
<h3 className="text-center font-semibold leading-tight">Zap</h3>
|
||||
<p className="text-sm text-blue-500 font-medium leading-tight">
|
||||
@ Someone love your note
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<User
|
||||
pubkey={event.pubkey}
|
||||
variant="notify2"
|
||||
subtext={`${compactNumber.format(invoice.amount)} sats`}
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-teal-500 font-medium">Zapped</p>
|
||||
<div className="flex-1 h-px bg-teal-300" />
|
||||
<div className="w-4 shrink-0 h-px bg-teal-300" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-col gap-3">
|
||||
<ActivityRootNote eventId={zapEventId} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -20,7 +20,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@ -34,7 +34,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@ -48,7 +48,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@ -62,7 +62,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@ -76,7 +76,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export function OnboardingModal() {
|
||||
return (
|
||||
<Dialog.Root open={onboarding}>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-xl dark:bg-white/20" />
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
|
||||
<div className="relative w-full max-w-lg bg-white h-[500px] rounded-xl dark:bg-black overflow-hidden">
|
||||
<OnboardingRouter />
|
||||
|
@ -2,6 +2,7 @@ import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import updateLocale from "dayjs/plugin/updateLocale";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { AUDIOS, IMAGES, VIDEOS } from "./constants";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(updateLocale);
|
||||
@ -68,3 +69,23 @@ export const compactNumber = Intl.NumberFormat("en", { notation: "compact" });
|
||||
|
||||
// country name
|
||||
export const regionNames = new Intl.DisplayNames(["en"], { type: "language" });
|
||||
|
||||
// verify link can be preview
|
||||
export function canPreview(text: string) {
|
||||
const url = new URL(text);
|
||||
const ext = url.pathname.split(".").pop();
|
||||
const hostname = url.hostname;
|
||||
|
||||
if (VIDEOS.includes(ext)) return false;
|
||||
if (IMAGES.includes(ext)) return false;
|
||||
if (AUDIOS.includes(ext)) return false;
|
||||
|
||||
if (hostname === "youtube.com") return false;
|
||||
if (hostname === "youtu.be") return false;
|
||||
if (hostname === "x.com") return false;
|
||||
if (hostname === "twitter.com") return false;
|
||||
if (hostname === "facebook.com") return false;
|
||||
if (hostname === "vimeo.com") return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
480
pnpm-lock.yaml
480
pnpm-lock.yaml
@ -270,25 +270,25 @@ importers:
|
||||
version: 0.15.0(@nostr-dev-kit/ndk@2.3.2)(nostr-fetch@0.15.0)
|
||||
'@radix-ui/react-avatar':
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 1.0.4(@types/react@18.2.47)(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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 1.0.3(@types/react@18.2.47)(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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 1.0.5(@types/react@18.2.47)(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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 2.0.6(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-hover-card':
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 1.0.7(@types/react@18.2.47)(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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.17.9
|
||||
version: 5.17.9(react@18.2.0)
|
||||
@ -1927,6 +1927,26 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-arrow@1.0.3(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
|
||||
peerDependencies:
|
||||
@ -1951,6 +1971,29 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-avatar@1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
|
||||
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.23.8
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
|
||||
peerDependencies:
|
||||
@ -1979,6 +2022,33 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-collapsible@1.0.3(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
|
||||
peerDependencies:
|
||||
@ -2003,6 +2073,29 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-collection@1.0.3(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.47)(react@18.2.0):
|
||||
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
|
||||
peerDependencies:
|
||||
@ -2065,6 +2158,39 @@ packages:
|
||||
react-remove-scroll: 2.5.5(@types/react@18.2.47)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-dialog@1.0.5(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.47)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-direction@1.0.1(@types/react@18.2.47)(react@18.2.0):
|
||||
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
|
||||
peerDependencies:
|
||||
@ -2104,6 +2230,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.47)(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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
|
||||
peerDependencies:
|
||||
@ -2131,6 +2281,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.47)(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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-menu': 2.0.6(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.47)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
|
||||
peerDependencies:
|
||||
@ -2168,6 +2344,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.47)(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.23.8
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==}
|
||||
peerDependencies:
|
||||
@ -2197,6 +2395,34 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-hover-card@1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==}
|
||||
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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-id@1.0.1(@types/react@18.2.47)(react@18.2.0):
|
||||
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
|
||||
peerDependencies:
|
||||
@ -2250,6 +2476,43 @@ packages:
|
||||
react-remove-scroll: 2.5.5(@types/react@18.2.47)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-menu@2.0.6(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-collection': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-roving-focus': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.47)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
|
||||
peerDependencies:
|
||||
@ -2285,6 +2548,40 @@ packages:
|
||||
react-remove-scroll: 2.5.5(@types/react@18.2.47)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popover@1.0.7(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.47)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
|
||||
peerDependencies:
|
||||
@ -2315,6 +2612,35 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-popper@1.1.3(@types/react@18.2.47)(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.23.8
|
||||
'@floating-ui/react-dom': 2.0.5(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-arrow': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/rect': 1.0.1
|
||||
'@types/react': 18.2.47
|
||||
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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
|
||||
peerDependencies:
|
||||
@ -2336,6 +2662,26 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-portal@1.0.4(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
|
||||
peerDependencies:
|
||||
@ -2358,6 +2704,27 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-presence@1.0.1(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
|
||||
peerDependencies:
|
||||
@ -2379,6 +2746,26 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-primitive@1.0.3(@types/react@18.2.47)(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.23.8
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
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.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
|
||||
peerDependencies:
|
||||
@ -2408,6 +2795,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.47)(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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-collection': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-select@2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==}
|
||||
peerDependencies:
|
||||
@ -2523,6 +2938,37 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-tooltip@1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==}
|
||||
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.23.8
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-visually-hidden': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.47)(react@18.2.0):
|
||||
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
|
||||
peerDependencies:
|
||||
@ -2646,6 +3092,26 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-visually-hidden@1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==}
|
||||
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.23.8
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/react': 18.2.47
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@radix-ui/rect@1.0.1:
|
||||
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
|
||||
dependencies:
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::process::Command;
|
||||
use keyring::Entry;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use webpage::{Webpage, WebpageOptions};
|
||||
|
||||
@ -117,14 +117,14 @@ pub async fn opengraph(url: String) -> OpenGraphResponse {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn secure_save(key: String, value: String) -> Result<(), ()> {
|
||||
let entry = Entry::new("lume", &key).expect("Failed to create entry");
|
||||
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
||||
let _ = entry.set_password(&value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn secure_load(key: String) -> Result<String, String> {
|
||||
let entry = Entry::new("lume", &key).expect("Failed to create entry");
|
||||
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
||||
if let Ok(password) = entry.get_password() {
|
||||
Ok(password)
|
||||
} else {
|
||||
@ -134,7 +134,7 @@ pub fn secure_load(key: String) -> Result<String, String> {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn secure_remove(key: String) -> Result<(), ()> {
|
||||
let entry = Entry::new("lume", &key).expect("Failed to create entry");
|
||||
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
||||
let _ = entry.delete_password();
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user