mirror of
https://github.com/lumehq/lume.git
synced 2025-03-28 18:52:33 +01:00
feat: use native feature instead of react
This commit is contained in:
parent
59eaaec903
commit
1283432632
@ -15,10 +15,6 @@
|
|||||||
"@lume/utils": "workspace:^",
|
"@lume/utils": "workspace:^",
|
||||||
"@radix-ui/react-avatar": "^1.0.4",
|
"@radix-ui/react-avatar": "^1.0.4",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
"@radix-ui/react-collapsible": "^1.0.3",
|
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
|
||||||
"@radix-ui/react-hover-card": "^1.0.7",
|
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
"@radix-ui/react-scroll-area": "^1.1.0",
|
"@radix-ui/react-scroll-area": "^1.1.0",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
@ -37,12 +33,10 @@
|
|||||||
"react-currency-input-field": "^3.8.0",
|
"react-currency-input-field": "^3.8.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.52.0",
|
"react-hook-form": "^7.52.0",
|
||||||
"react-hotkeys-hook": "^4.5.0",
|
|
||||||
"react-i18next": "^14.1.2",
|
"react-i18next": "^14.1.2",
|
||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"slate": "^0.103.0",
|
"slate": "^0.103.0",
|
||||||
"slate-react": "^0.105.0",
|
"slate-react": "^0.105.0",
|
||||||
"sonner": "^1.5.0",
|
|
||||||
"use-debounce": "^10.0.1",
|
"use-debounce": "^10.0.1",
|
||||||
"virtua": "^0.31.0"
|
"virtua": "^0.31.0"
|
||||||
},
|
},
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { NostrQuery } from "@lume/system";
|
import { NostrQuery } from "@lume/system";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import {
|
import {
|
||||||
type Dispatch,
|
type Dispatch,
|
||||||
type ReactNode,
|
type ReactNode,
|
||||||
type SetStateAction,
|
type SetStateAction,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export function AvatarUploader({
|
export function AvatarUploader({
|
||||||
setPicture,
|
setPicture,
|
||||||
@ -27,7 +27,7 @@ export function AvatarUploader({
|
|||||||
setPicture(image);
|
setPicture(image);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), { title: "Lume", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import { Spinner } from "@lume/ui";
|
|||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
import { useNoteContext } from "../provider";
|
import { useNoteContext } from "../provider";
|
||||||
|
|
||||||
export function NoteRepost({ large = false }: { large?: boolean }) {
|
export function NoteRepost({ large = false }: { large?: boolean }) {
|
||||||
@ -27,12 +27,12 @@ export function NoteRepost({ large = false }: { large?: boolean }) {
|
|||||||
// update state
|
// update state
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setIsRepost(true);
|
setIsRepost(true);
|
||||||
|
|
||||||
// notify
|
|
||||||
toast.success("You've reposted this post successfully");
|
|
||||||
} catch {
|
} catch {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error("Repost failed, try again later");
|
await message("Repost failed, try again later", {
|
||||||
|
title: "Lume",
|
||||||
|
kind: "info",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,8 +87,7 @@ export function NoteContent({
|
|||||||
));
|
));
|
||||||
|
|
||||||
return richContent;
|
return richContent;
|
||||||
} catch (e) {
|
} catch {
|
||||||
console.log("[parser]: ", e);
|
|
||||||
return event.content;
|
return event.content;
|
||||||
}
|
}
|
||||||
}, [event.content]);
|
}, [event.content]);
|
||||||
|
@ -1,62 +1,62 @@
|
|||||||
|
import { LumeWindow } from "@lume/system";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
||||||
|
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||||
|
import { useCallback } from "react";
|
||||||
import { User } from "../user";
|
import { User } from "../user";
|
||||||
import { useNoteContext } from "./provider";
|
import { useNoteContext } from "./provider";
|
||||||
import { LumeWindow } from "@lume/system";
|
|
||||||
|
|
||||||
export function NoteUser({ className }: { className?: string }) {
|
export function NoteUser({ className }: { className?: string }) {
|
||||||
const event = useNoteContext();
|
const event = useNoteContext();
|
||||||
|
|
||||||
|
const showContextMenu = useCallback(async (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const menuItems = await Promise.all([
|
||||||
|
MenuItem.new({
|
||||||
|
text: "View Profile",
|
||||||
|
action: () => LumeWindow.openProfile(event.pubkey),
|
||||||
|
}),
|
||||||
|
MenuItem.new({
|
||||||
|
text: "Copy Public Key",
|
||||||
|
action: async () => {
|
||||||
|
const pubkey = await event.pubkeyAsBech32();
|
||||||
|
await writeText(pubkey);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const menu = await Menu.new({
|
||||||
|
items: menuItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
await menu.popup().catch((e) => console.error(e));
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<User.Provider pubkey={event.pubkey}>
|
<User.Provider pubkey={event.pubkey}>
|
||||||
<HoverCard.Root>
|
<User.Root className={cn("flex items-start justify-between", className)}>
|
||||||
<User.Root
|
<div className="flex w-full gap-2">
|
||||||
className={cn("flex items-start justify-between", className)}
|
<button
|
||||||
>
|
type="button"
|
||||||
<div className="flex w-full gap-2">
|
onClick={(e) => showContextMenu(e)}
|
||||||
<HoverCard.Trigger className="shrink-0">
|
className="shrink-0"
|
||||||
<User.Avatar className="object-cover rounded-full size-8 outline outline-1 -outline-offset-1 outline-black/15" />
|
|
||||||
</HoverCard.Trigger>
|
|
||||||
<div className="flex items-center w-full gap-3">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<User.Name className="font-semibold text-neutral-950 dark:text-neutral-50" />
|
|
||||||
<User.NIP05 />
|
|
||||||
</div>
|
|
||||||
<div className="text-neutral-600 dark:text-neutral-400">·</div>
|
|
||||||
<User.Time
|
|
||||||
time={event.created_at}
|
|
||||||
className="text-neutral-600 dark:text-neutral-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</User.Root>
|
|
||||||
<HoverCard.Portal>
|
|
||||||
<HoverCard.Content
|
|
||||||
className="w-[300px] rounded-xl bg-black p-3 data-[side=bottom]:animate-slideUpAndFade data-[state=open]:transition-all dark:bg-white dark:shadow-none"
|
|
||||||
sideOffset={5}
|
|
||||||
side="right"
|
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<User.Avatar className="object-cover rounded-full size-8 outline outline-1 -outline-offset-1 outline-black/15" />
|
||||||
<User.Avatar className="object-cover rounded-lg size-11" />
|
</button>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex items-center w-full gap-3">
|
||||||
<div className="inline-flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<User.Name className="font-semibold leading-tight text-white dark:text-neutral-900" />
|
<User.Name className="font-semibold text-neutral-950 dark:text-neutral-50" />
|
||||||
<User.NIP05 />
|
<User.NIP05 />
|
||||||
</div>
|
|
||||||
<User.About className="text-sm text-white line-clamp-3 dark:text-neutral-900" />
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => LumeWindow.openProfile(event.pubkey)}
|
|
||||||
className="inline-flex items-center justify-center w-full mt-2 text-sm font-medium bg-white rounded-lg h-9 hover:bg-neutral-200 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-neutral-200"
|
|
||||||
>
|
|
||||||
View profile
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<HoverCard.Arrow className="fill-black dark:fill-white" />
|
<div className="text-neutral-600 dark:text-neutral-400">·</div>
|
||||||
</HoverCard.Content>
|
<User.Time
|
||||||
</HoverCard.Portal>
|
time={event.created_at}
|
||||||
</HoverCard.Root>
|
className="text-neutral-600 dark:text-neutral-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</User.Root>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
import {
|
import { ComposeFilledIcon, HorizontalDotsIcon, PlusIcon } from "@lume/icons";
|
||||||
ComposeFilledIcon,
|
|
||||||
HorizontalDotsIcon,
|
|
||||||
PlusIcon,
|
|
||||||
SearchIcon,
|
|
||||||
} from "@lume/icons";
|
|
||||||
import { LumeWindow, NostrAccount } from "@lume/system";
|
import { LumeWindow, NostrAccount } from "@lume/system";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import * as Popover from "@radix-ui/react-popover";
|
import * as Popover from "@radix-ui/react-popover";
|
||||||
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
|
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
||||||
import { getCurrent } from "@tauri-apps/api/window";
|
import { getCurrent } from "@tauri-apps/api/window";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { toast } from "sonner";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
export const Route = createFileRoute("/$account")({
|
export const Route = createFileRoute("/$account")({
|
||||||
beforeLoad: async () => {
|
beforeLoad: async () => {
|
||||||
@ -63,12 +59,12 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Accounts() {
|
function Accounts() {
|
||||||
|
const navigate = Route.useNavigate();
|
||||||
const { accounts } = Route.useRouteContext();
|
const { accounts } = Route.useRouteContext();
|
||||||
const { account } = Route.useParams();
|
const { account } = Route.useParams();
|
||||||
|
|
||||||
const [windowWidth, setWindowWidth] = useState<number>(null);
|
const [windowWidth, setWindowWidth] = useState<number>(null);
|
||||||
|
|
||||||
const navigate = Route.useNavigate();
|
|
||||||
const sortedList = useMemo(() => {
|
const sortedList = useMemo(() => {
|
||||||
const list = accounts;
|
const list = accounts;
|
||||||
|
|
||||||
@ -82,9 +78,33 @@ function Accounts() {
|
|||||||
return list;
|
return list;
|
||||||
}, [accounts]);
|
}, [accounts]);
|
||||||
|
|
||||||
const changeAccount = async (npub: string) => {
|
const showContextMenu = useCallback(
|
||||||
|
async (e: React.MouseEvent, npub: string) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const menuItems = await Promise.all([
|
||||||
|
MenuItem.new({
|
||||||
|
text: "View Profile",
|
||||||
|
action: () => LumeWindow.openProfile(npub),
|
||||||
|
}),
|
||||||
|
MenuItem.new({
|
||||||
|
text: "Open Settings",
|
||||||
|
action: () => LumeWindow.openSettings(),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const menu = await Menu.new({
|
||||||
|
items: menuItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
await menu.popup().catch((e) => console.error(e));
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const changeAccount = async (e: React.MouseEvent, npub: string) => {
|
||||||
if (npub === account) {
|
if (npub === account) {
|
||||||
return await LumeWindow.openProfile(account);
|
return showContextMenu(e, npub);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change current account and update signer
|
// Change current account and update signer
|
||||||
@ -102,7 +122,7 @@ function Accounts() {
|
|||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.warning("Something wrong.");
|
await message("Something wrong.", { title: "Accounts", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,7 +155,11 @@ function Accounts() {
|
|||||||
{sortedList
|
{sortedList
|
||||||
.slice(0, windowWidth > 500 ? account.length : 2)
|
.slice(0, windowWidth > 500 ? account.length : 2)
|
||||||
.map((user) => (
|
.map((user) => (
|
||||||
<button key={user} type="button" onClick={() => changeAccount(user)}>
|
<button
|
||||||
|
key={user}
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => changeAccount(e, user)}
|
||||||
|
>
|
||||||
<User.Provider pubkey={user}>
|
<User.Provider pubkey={user}>
|
||||||
<User.Root
|
<User.Root
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -161,12 +185,12 @@ function Accounts() {
|
|||||||
<HorizontalDotsIcon className="size-5" />
|
<HorizontalDotsIcon className="size-5" />
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Portal>
|
<Popover.Portal>
|
||||||
<Popover.Content className="flex h-11 select-none items-center justify-center rounded-md bg-neutral-950 p-1 text-sm text-neutral-50 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade dark:bg-neutral-50 dark:text-neutral-950">
|
<Popover.Content className="flex h-11 select-none items-center justify-center rounded-md bg-black/20 p-1 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
|
||||||
{sortedList.slice(2).map((user) => (
|
{sortedList.slice(2).map((user) => (
|
||||||
<button
|
<button
|
||||||
key={user}
|
key={user}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => changeAccount(user)}
|
onClick={(e) => changeAccount(e, user)}
|
||||||
className="inline-flex items-center justify-center rounded-md size-9 hover:bg-white/10"
|
className="inline-flex items-center justify-center rounded-md size-9 hover:bg-white/10"
|
||||||
>
|
>
|
||||||
<User.Provider pubkey={user}>
|
<User.Provider pubkey={user}>
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import { CancelCircleIcon, CheckCircleIcon, InfoCircleIcon } from "@lume/icons";
|
|
||||||
import type { Settings } from "@lume/system";
|
import type { Settings } from "@lume/system";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import type { QueryClient } from "@tanstack/react-query";
|
import type { QueryClient } from "@tanstack/react-query";
|
||||||
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
|
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
|
||||||
import type { Platform } from "@tauri-apps/plugin-os";
|
import type { Platform } from "@tauri-apps/plugin-os";
|
||||||
import { Toaster } from "sonner";
|
|
||||||
|
|
||||||
interface RouterContext {
|
interface RouterContext {
|
||||||
// System
|
// System
|
||||||
@ -19,21 +17,7 @@ interface RouterContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Route = createRootRouteWithContext<RouterContext>()({
|
export const Route = createRootRouteWithContext<RouterContext>()({
|
||||||
component: () => (
|
component: () => <Outlet />,
|
||||||
<>
|
|
||||||
<Toaster
|
|
||||||
position="bottom-right"
|
|
||||||
icons={{
|
|
||||||
success: <CheckCircleIcon className="size-5" />,
|
|
||||||
info: <InfoCircleIcon className="size-5" />,
|
|
||||||
error: <CancelCircleIcon className="size-5" />,
|
|
||||||
}}
|
|
||||||
closeButton
|
|
||||||
theme="system"
|
|
||||||
/>
|
|
||||||
<Outlet />
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
pendingComponent: Pending,
|
pendingComponent: Pending,
|
||||||
wrapInSuspense: true,
|
wrapInSuspense: true,
|
||||||
});
|
});
|
||||||
|
@ -5,9 +5,9 @@ import * as Checkbox from "@radix-ui/react-checkbox";
|
|||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/auth/$account/backup")({
|
export const Route = createFileRoute("/auth/$account/backup")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@ -29,7 +29,10 @@ function Screen() {
|
|||||||
try {
|
try {
|
||||||
if (key) {
|
if (key) {
|
||||||
if (!confirm.c1 || !confirm.c2 || !confirm.c3) {
|
if (!confirm.c1 || !confirm.c2 || !confirm.c3) {
|
||||||
return toast.warning("You need to confirm before continue");
|
return await message("You need to confirm before continue", {
|
||||||
|
title: "Backup",
|
||||||
|
kind: "info",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate({ to: "/", replace: true });
|
navigate({ to: "/", replace: true });
|
||||||
@ -48,7 +51,10 @@ function Screen() {
|
|||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), {
|
||||||
|
title: "Backup",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,7 +63,10 @@ function Screen() {
|
|||||||
await writeText(key);
|
await writeText(key);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error(e);
|
await message(String(e), {
|
||||||
|
title: "Backup",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,10 +4,10 @@ import { NostrAccount } from "@lume/system";
|
|||||||
import type { Metadata } from "@lume/types";
|
import type { Metadata } from "@lume/types";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/auth/create-profile")({
|
export const Route = createFileRoute("/auth/create-profile")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@ -53,7 +53,7 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), { title: "Create Profile", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { NostrAccount } from "@lume/system";
|
import { NostrAccount } from "@lume/system";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/auth/import")({
|
export const Route = createLazyFileRoute("/auth/import")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@ -16,10 +16,12 @@ function Screen() {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
if (!key.startsWith("nsec1"))
|
if (!key.startsWith("nsec1")) {
|
||||||
return toast.warning(
|
return await message(
|
||||||
"You need to enter a valid private key starts with nsec or ncryptsec",
|
"You need to enter a valid private key starts with nsec or ncryptsec",
|
||||||
|
{ title: "Import Key", kind: "info" },
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -31,7 +33,7 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(e);
|
await message(String(e), { title: "Import Key", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { NostrAccount } from "@lume/system";
|
import { NostrAccount } from "@lume/system";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/auth/remote")({
|
export const Route = createLazyFileRoute("/auth/remote")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@ -15,10 +15,12 @@ function Screen() {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
if (!uri.startsWith("bunker://"))
|
if (!uri.startsWith("bunker://")) {
|
||||||
return toast.warning(
|
return await message(
|
||||||
"You need to enter a valid Connect URI starts with bunker://",
|
"You need to enter a valid Connect URI starts with bunker://",
|
||||||
|
{ title: "Nostr Connect", kind: "info" },
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -30,7 +32,7 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(e);
|
await message(String(e), { title: "Nostr Connect", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ import { NostrQuery } from "@lume/system";
|
|||||||
import type { Relay } from "@lume/types";
|
import type { Relay } from "@lume/types";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/bootstrap-relays")({
|
export const Route = createFileRoute("/bootstrap-relays")({
|
||||||
loader: async () => {
|
loader: async () => {
|
||||||
@ -32,7 +32,7 @@ function Screen() {
|
|||||||
setRelays((prev) => [...prev, relay]);
|
setRelays((prev) => [...prev, relay]);
|
||||||
reset();
|
reset();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error(String(e));
|
await message(String(e), { title: "Bootstrap Relays", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,8 +41,7 @@ function Screen() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await NostrQuery.saveBootstrapRelays(relays);
|
await NostrQuery.saveBootstrapRelays(relays);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
await message(String(e), { title: "Bootstrap Relays", kind: "error" });
|
||||||
toast.error(String(e));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
|
import { User } from "@/components/user";
|
||||||
import { CancelIcon, PlusIcon } from "@lume/icons";
|
import { CancelIcon, PlusIcon } from "@lume/icons";
|
||||||
|
import { NostrAccount, NostrQuery } from "@lume/system";
|
||||||
import type { ColumnRouteSearch } from "@lume/types";
|
import type { ColumnRouteSearch } from "@lume/types";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { User } from "@/components/user";
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
import { NostrAccount, NostrQuery } from "@lume/system";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/create-group")({
|
export const Route = createFileRoute("/create-group")({
|
||||||
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
||||||
@ -65,25 +65,25 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
toast.error(e);
|
await message(String(e), { title: "Create Group", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full flex flex-col items-center justify-center gap-4">
|
<div className="flex flex-col items-center justify-center w-full h-full gap-4">
|
||||||
<div className="text-center flex flex-col items-center justify-center">
|
<div className="flex flex-col items-center justify-center text-center">
|
||||||
<h1 className="text-2xl font-serif font-medium">
|
<h1 className="font-serif text-2xl font-medium">
|
||||||
Focus feeds for people you like
|
Focus feeds for people you like
|
||||||
</h1>
|
</h1>
|
||||||
<p className="leading-tight text-neutral-700 dark:text-neutral-300">
|
<p className="leading-tight text-neutral-700 dark:text-neutral-300">
|
||||||
Add some people for custom feeds.
|
Add some people for custom feeds.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-4/5 max-w-full flex flex-col gap-3">
|
<div className="flex flex-col w-4/5 max-w-full gap-3">
|
||||||
<div className="w-full h-9 shrink-0 flex items-center bg-black/5 dark:bg-white/5 rounded-lg">
|
<div className="flex items-center w-full rounded-lg h-9 shrink-0 bg-black/5 dark:bg-white/5">
|
||||||
<label
|
<label
|
||||||
htmlFor="name"
|
htmlFor="name"
|
||||||
className="w-16 border-r border-black/10 dark:border-white/10 shrink-0 text-center text-sm font-semibold"
|
className="w-16 text-sm font-semibold text-center border-r border-black/10 dark:border-white/10 shrink-0"
|
||||||
>
|
>
|
||||||
Name
|
Name
|
||||||
</label>
|
</label>
|
||||||
@ -92,10 +92,10 @@ function Screen() {
|
|||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
placeholder="Enter a name for this group"
|
placeholder="Enter a name for this group"
|
||||||
className="h-full bg-transparent border-none text-sm px-3 placeholder:text-neutral-600 focus:border-neutral-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
className="h-full px-3 text-sm bg-transparent border-none placeholder:text-neutral-600 focus:border-neutral-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center w-full gap-3">
|
||||||
<div className="overflow-y-auto scrollbar-none p-2 w-full h-[450px] flex flex-col gap-3 bg-black/5 dark:bg-white/5 backdrop-blur-lg rounded-xl">
|
<div className="overflow-y-auto scrollbar-none p-2 w-full h-[450px] flex flex-col gap-3 bg-black/5 dark:bg-white/5 backdrop-blur-lg rounded-xl">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<input
|
<input
|
||||||
@ -103,12 +103,12 @@ function Screen() {
|
|||||||
value={npub}
|
value={npub}
|
||||||
onChange={(e) => setNpub(e.target.value)}
|
onChange={(e) => setNpub(e.target.value)}
|
||||||
placeholder="npub1..."
|
placeholder="npub1..."
|
||||||
className="h-9 w-full rounded-lg bg-black/10 dark:bg-white/10 border-none text-sm px-3 placeholder:text-neutral-600 focus:border-neutral-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
className="w-full px-3 text-sm border-none rounded-lg h-9 bg-black/10 dark:bg-white/10 placeholder:text-neutral-600 focus:border-neutral-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => addUser()}
|
onClick={() => addUser()}
|
||||||
className="inline-flex size-9 rounded-lg items-center justify-center bg-black/20 dark:bg-white/20 shrink-0 text-white hover:bg-blue-500"
|
className="inline-flex items-center justify-center text-white rounded-lg size-9 bg-black/20 dark:bg-white/20 shrink-0 hover:bg-blue-500"
|
||||||
>
|
>
|
||||||
<PlusIcon className="size-6" />
|
<PlusIcon className="size-6" />
|
||||||
</button>
|
</button>
|
||||||
@ -122,11 +122,11 @@ function Screen() {
|
|||||||
key={item}
|
key={item}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => toggleUser(item)}
|
onClick={() => toggleUser(item)}
|
||||||
className="inline-flex items-center justify-between px-3 py-2 rounded-lg bg-white dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50"
|
className="inline-flex items-center justify-between px-3 py-2 bg-white rounded-lg dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50"
|
||||||
>
|
>
|
||||||
<User.Provider pubkey={item}>
|
<User.Provider pubkey={item}>
|
||||||
<User.Root className="flex items-center gap-2.5">
|
<User.Root className="flex items-center gap-2.5">
|
||||||
<User.Avatar className="size-8 rounded-full object-cover" />
|
<User.Avatar className="object-cover rounded-full size-8" />
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<User.Name className="text-sm font-medium" />
|
<User.Name className="text-sm font-medium" />
|
||||||
</div>
|
</div>
|
||||||
@ -138,7 +138,7 @@ function Screen() {
|
|||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-black/5 dark:bg-white/5 text-sm flex items-center justify-center h-14 rounded-lg">
|
<div className="flex items-center justify-center text-sm rounded-lg bg-black/5 dark:bg-white/5 h-14">
|
||||||
Empty.
|
Empty.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -153,11 +153,11 @@ function Screen() {
|
|||||||
key={item}
|
key={item}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => toggleUser(item)}
|
onClick={() => toggleUser(item)}
|
||||||
className="inline-flex items-center justify-between px-3 py-2 rounded-lg bg-white dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50"
|
className="inline-flex items-center justify-between px-3 py-2 bg-white rounded-lg dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50"
|
||||||
>
|
>
|
||||||
<User.Provider pubkey={item}>
|
<User.Provider pubkey={item}>
|
||||||
<User.Root className="flex items-center gap-2.5">
|
<User.Root className="flex items-center gap-2.5">
|
||||||
<User.Avatar className="size-8 rounded-full object-cover" />
|
<User.Avatar className="object-cover rounded-full size-8" />
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<User.Name className="text-sm font-medium" />
|
<User.Name className="text-sm font-medium" />
|
||||||
</div>
|
</div>
|
||||||
@ -166,7 +166,7 @@ function Screen() {
|
|||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-black/5 dark:bg-white/5 text-sm flex items-center justify-center h-14 rounded-lg">
|
<div className="flex items-center justify-center text-sm rounded-lg bg-black/5 dark:bg-white/5 h-14">
|
||||||
<p>
|
<p>
|
||||||
Find more user at{" "}
|
Find more user at{" "}
|
||||||
<a
|
<a
|
||||||
@ -187,7 +187,7 @@ function Screen() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
disabled={isLoading || users.length < 1}
|
disabled={isLoading || users.length < 1}
|
||||||
className="inline-flex items-center justify-center w-36 rounded-full h-9 bg-blue-500 text-white text-sm font-medium hover:bg-blue-600 disabled:opacity-50"
|
className="inline-flex items-center justify-center text-sm font-medium text-white bg-blue-500 rounded-full w-36 h-9 hover:bg-blue-600 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{isLoading ? <Spinner /> : "Confirm"}
|
{isLoading ? <Spinner /> : "Confirm"}
|
||||||
</button>
|
</button>
|
||||||
|
@ -2,8 +2,8 @@ import { NostrAccount } from "@lume/system";
|
|||||||
import type { ColumnRouteSearch } from "@lume/types";
|
import type { ColumnRouteSearch } from "@lume/types";
|
||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/create-newsfeed/f2f")({
|
export const Route = createFileRoute("/create-newsfeed/f2f")({
|
||||||
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
||||||
@ -24,8 +24,12 @@ function Screen() {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
if (!npub.startsWith("npub1"))
|
if (!npub.startsWith("npub1")) {
|
||||||
return toast.warning("You must enter a valid npub.");
|
return await message("You must enter a valid npub.", {
|
||||||
|
title: "Create Newsfeed",
|
||||||
|
kind: "info",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@ -37,13 +41,16 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), {
|
||||||
|
title: "Create Newsfeed",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-y-auto scrollbar-none p-2 shrink-0 h-[450px] bg-white dark:bg-white/20 backdrop-blur-lg rounded-xl shadow-primary dark:ring-1 ring-neutral-800/50">
|
<div className="overflow-y-auto scrollbar-none p-2 shrink-0 h-[450px] bg-white dark:bg-white/20 backdrop-blur-lg rounded-xl shadow-primary dark:ring-1 ring-neutral-800/50">
|
||||||
<div className="h-full flex flex-col justify-between">
|
<div className="flex flex-col justify-between h-full">
|
||||||
<div className="flex-1 flex flex-col gap-1.5 justify-center px-5">
|
<div className="flex-1 flex flex-col gap-1.5 justify-center px-5">
|
||||||
<p className="font-semibold text-neutral-500">
|
<p className="font-semibold text-neutral-500">
|
||||||
You already have a friend on Nostr?
|
You already have a friend on Nostr?
|
||||||
@ -60,7 +67,7 @@ function Screen() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<label htmlFor="npub" className="font-medium text-sm">
|
<label htmlFor="npub" className="text-sm font-medium">
|
||||||
NPUB
|
NPUB
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -69,13 +76,13 @@ function Screen() {
|
|||||||
value={npub}
|
value={npub}
|
||||||
onChange={(e) => setNpub(e.target.value)}
|
onChange={(e) => setNpub(e.target.value)}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="h-11 rounded-lg bg-transparent border border-neutral-200 dark:border-neutral-800 px-3 placeholder:text-neutral-600 focus:border-blue-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
className="px-3 bg-transparent border rounded-lg h-11 border-neutral-200 dark:border-neutral-800 placeholder:text-neutral-600 focus:border-blue-500 focus:ring-0 dark:placeholder:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
className="inline-flex items-center justify-center w-full rounded-lg h-9 bg-blue-500 text-white text-sm font-medium hover:bg-blue-600"
|
className="inline-flex items-center justify-center w-full text-sm font-medium text-white bg-blue-500 rounded-lg h-9 hover:bg-blue-600"
|
||||||
>
|
>
|
||||||
{isLoading ? <Spinner /> : "Confirm"}
|
{isLoading ? <Spinner /> : "Confirm"}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
import { Suspense, useState } from "react";
|
|
||||||
import { Await, defer } from "@tanstack/react-router";
|
|
||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
import { Spinner } from "@lume/ui";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
import type { ColumnRouteSearch } from "@lume/types";
|
|
||||||
import { NostrAccount } from "@lume/system";
|
import { NostrAccount } from "@lume/system";
|
||||||
|
import type { ColumnRouteSearch } from "@lume/types";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { Await, defer } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
|
import { Suspense, useState } from "react";
|
||||||
|
|
||||||
export const Route = createFileRoute("/create-newsfeed/users")({
|
export const Route = createFileRoute("/create-newsfeed/users")({
|
||||||
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
validateSearch: (search: Record<string, string>): ColumnRouteSearch => {
|
||||||
@ -59,16 +59,19 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), {
|
||||||
|
title: "Create Group",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center w-full gap-3">
|
||||||
<div className="overflow-y-auto scrollbar-none p-2 w-full h-[450px] bg-black/5 dark:bg-white/5 backdrop-blur-lg rounded-xl">
|
<div className="overflow-y-auto scrollbar-none p-2 w-full h-[450px] bg-black/5 dark:bg-white/5 backdrop-blur-lg rounded-xl">
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={
|
fallback={
|
||||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
<div className="flex flex-col items-center justify-center w-full h-20 gap-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex items-center gap-2 text-sm font-medium"
|
className="inline-flex items-center gap-2 text-sm font-medium"
|
||||||
@ -85,27 +88,27 @@ function Screen() {
|
|||||||
users.profiles.map((item: { pubkey: string }) => (
|
users.profiles.map((item: { pubkey: string }) => (
|
||||||
<div
|
<div
|
||||||
key={item.pubkey}
|
key={item.pubkey}
|
||||||
className="h-max w-full overflow-hidden mb-2 p-2 bg-white dark:bg-black/20 backdrop-blur-lg rounded-lg shadow-primary dark:ring-1 ring-neutral-800/50"
|
className="w-full p-2 mb-2 overflow-hidden bg-white rounded-lg h-max dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50"
|
||||||
>
|
>
|
||||||
<User.Provider pubkey={item.pubkey}>
|
<User.Provider pubkey={item.pubkey}>
|
||||||
<User.Root>
|
<User.Root>
|
||||||
<div className="flex h-full w-full flex-col gap-2">
|
<div className="flex flex-col w-full h-full gap-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<User.Avatar className="size-7 shrink-0 rounded-full object-cover" />
|
<User.Avatar className="object-cover rounded-full size-7 shrink-0" />
|
||||||
<User.Name className="text-sm leadning-tight max-w-[15rem] truncate font-semibold" />
|
<User.Name className="text-sm leadning-tight max-w-[15rem] truncate font-semibold" />
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => toggleFollow(item.pubkey)}
|
onClick={() => toggleFollow(item.pubkey)}
|
||||||
className="inline-flex h-7 w-20 items-center justify-center rounded-lg bg-black/10 text-sm font-medium hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20"
|
className="inline-flex items-center justify-center w-20 text-sm font-medium rounded-lg h-7 bg-black/10 hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20"
|
||||||
>
|
>
|
||||||
{follows.includes(item.pubkey)
|
{follows.includes(item.pubkey)
|
||||||
? "Unfollow"
|
? "Unfollow"
|
||||||
: "Follow"}
|
: "Follow"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<User.About className="line-clamp-3 max-w-none select-text text-neutral-800 dark:text-neutral-400" />
|
<User.About className="select-text line-clamp-3 max-w-none text-neutral-800 dark:text-neutral-400" />
|
||||||
</div>
|
</div>
|
||||||
</User.Root>
|
</User.Root>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
@ -119,7 +122,7 @@ function Screen() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
disabled={isLoading || follows.length < 1}
|
disabled={isLoading || follows.length < 1}
|
||||||
className="inline-flex items-center justify-center w-36 rounded-full h-9 bg-blue-500 text-white text-sm font-medium hover:bg-blue-600 disabled:opacity-50"
|
className="inline-flex items-center justify-center text-sm font-medium text-white bg-blue-500 rounded-full w-36 h-9 hover:bg-blue-600 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{isLoading ? <Spinner /> : "Confirm"}
|
{isLoading ? <Spinner /> : "Confirm"}
|
||||||
</button>
|
</button>
|
||||||
|
@ -4,8 +4,8 @@ import type { ColumnRouteSearch } from "@lume/types";
|
|||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { TOPICS } from "@lume/utils";
|
import { TOPICS } from "@lume/utils";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
type Topic = {
|
type Topic = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -53,7 +53,10 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), {
|
||||||
|
title: "Create Topic",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ import { Spinner } from "@lume/ui";
|
|||||||
import { insertImage, isImagePath } from "@lume/utils";
|
import { insertImage, isImagePath } from "@lume/utils";
|
||||||
import type { UnlistenFn } from "@tauri-apps/api/event";
|
import type { UnlistenFn } from "@tauri-apps/api/event";
|
||||||
import { getCurrent } from "@tauri-apps/api/window";
|
import { getCurrent } from "@tauri-apps/api/window";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useSlateStatic } from "slate-react";
|
import { useSlateStatic } from "slate-react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export function MediaButton() {
|
export function MediaButton() {
|
||||||
const editor = useSlateStatic();
|
const editor = useSlateStatic();
|
||||||
@ -24,7 +24,7 @@ export function MediaButton() {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(`Upload failed, error: ${e}`);
|
await message(String(e), { title: "Upload", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { PlusIcon, RelayIcon } from "@lume/icons";
|
|
||||||
import { Spinner } from "@lume/ui";
|
|
||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
|
import { PlusIcon, RelayIcon } from "@lume/icons";
|
||||||
|
import { NostrAccount } from "@lume/system";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
import { checkForAppUpdates, displayNpub } from "@lume/utils";
|
import { checkForAppUpdates, displayNpub } from "@lume/utils";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
import { NostrAccount } from "@lume/system";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
beforeLoad: async () => {
|
beforeLoad: async () => {
|
||||||
@ -51,7 +51,10 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading({ npub: "", status: false });
|
setLoading({ npub: "", status: false });
|
||||||
toast.error(String(e));
|
await message(String(e), {
|
||||||
|
title: "Account",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { SearchIcon } from "@lume/icons";
|
|
||||||
import { type NostrEvent, Kind } from "@lume/types";
|
|
||||||
import { Spinner } from "@lume/ui";
|
|
||||||
import { Note } from "@/components/note";
|
import { Note } from "@/components/note";
|
||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { SearchIcon } from "@lume/icons";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
import { useDebounce } from "use-debounce";
|
|
||||||
import { LumeEvent, LumeWindow } from "@lume/system";
|
import { LumeEvent, LumeWindow } from "@lume/system";
|
||||||
|
import { Kind, type NostrEvent } from "@lume/types";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useDebounce } from "use-debounce";
|
||||||
|
|
||||||
export const Route = createFileRoute("/search")({
|
export const Route = createFileRoute("/search")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@ -34,7 +34,10 @@ function Screen() {
|
|||||||
setEvents(sorted);
|
setEvents(sorted);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), {
|
||||||
|
title: "Search",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
import { NostrAccount } from "@lume/system";
|
import { NostrAccount } from "@lume/system";
|
||||||
import { displayNpub, displayNsec } from "@lume/utils";
|
import { displayNpub } from "@lume/utils";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
interface Account {
|
interface Account {
|
||||||
npub: string;
|
npub: string;
|
||||||
@ -43,7 +43,7 @@ function Account({ account }: { account: string }) {
|
|||||||
await writeText(data);
|
await writeText(data);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error(e);
|
await message(String(e), { title: "Backup", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { CancelIcon, PlusIcon } from "@lume/icons";
|
import { CancelIcon, PlusIcon } from "@lume/icons";
|
||||||
import { NostrQuery } from "@lume/system";
|
import { NostrQuery } from "@lume/system";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/settings/relay")({
|
export const Route = createFileRoute("/settings/relay")({
|
||||||
loader: async () => {
|
loader: async () => {
|
||||||
@ -33,7 +33,7 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), { title: "Relay", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,22 +42,22 @@ function Screen() {
|
|||||||
}, [relayList]);
|
}, [relayList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-xl">
|
<div className="w-full max-w-xl mx-auto">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="font-semibold text-sm text-neutral-700 dark:text-neutral-300">
|
<h2 className="text-sm font-semibold text-neutral-700 dark:text-neutral-300">
|
||||||
Connected Relays
|
Connected Relays
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex flex-col divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl px-3">
|
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
{relays.map((relay) => (
|
{relays.map((relay) => (
|
||||||
<div
|
<div
|
||||||
key={relay}
|
key={relay}
|
||||||
className="flex justify-between items-center h-11"
|
className="flex items-center justify-between h-11"
|
||||||
>
|
>
|
||||||
<div className="inline-flex items-center gap-2 text-sm font-medium">
|
<div className="inline-flex items-center gap-2 text-sm font-medium">
|
||||||
<span className="relative flex size-2">
|
<span className="relative flex size-2">
|
||||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-teal-400 opacity-75"></span>
|
<span className="absolute inline-flex w-full h-full bg-teal-400 rounded-full opacity-75 animate-ping" />
|
||||||
<span className="relative inline-flex rounded-full size-2 bg-teal-500"></span>
|
<span className="relative inline-flex bg-teal-500 rounded-full size-2" />
|
||||||
</span>
|
</span>
|
||||||
{relay}
|
{relay}
|
||||||
</div>
|
</div>
|
||||||
@ -65,7 +65,7 @@ function Screen() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => NostrQuery.removeRelay(relay)}
|
onClick={() => NostrQuery.removeRelay(relay)}
|
||||||
className="inline-flex items-center justify-center size-7 rounded-md hover:bg-black/10 dark:hover:bg-white/10"
|
className="inline-flex items-center justify-center rounded-md size-7 hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
>
|
>
|
||||||
<CancelIcon className="size-4" />
|
<CancelIcon className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
@ -75,7 +75,7 @@ function Screen() {
|
|||||||
<div className="flex items-center h-14">
|
<div className="flex items-center h-14">
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
className="w-full flex items-center gap-2 mb-0"
|
className="flex items-center w-full gap-2 mb-0"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
{...register("url", {
|
{...register("url", {
|
||||||
@ -85,12 +85,12 @@ function Screen() {
|
|||||||
name="url"
|
name="url"
|
||||||
placeholder="wss://..."
|
placeholder="wss://..."
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="h-9 flex-1 rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring-0 dark:border-neutral-700 dark:placeholder:text-neutral-400"
|
className="flex-1 px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring-0 dark:border-neutral-700 dark:placeholder:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
className="shrink-0 inline-flex h-9 w-16 px-2 items-center justify-center rounded-lg bg-black/20 dark:bg-white/20 font-medium text-sm text-white hover:bg-blue-500 disabled:opacity-50"
|
className="inline-flex items-center justify-center w-16 px-2 text-sm font-medium text-white rounded-lg shrink-0 h-9 bg-black/20 dark:bg-white/20 hover:bg-blue-500 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<PlusIcon className="size-7" />
|
<PlusIcon className="size-7" />
|
||||||
</button>
|
</button>
|
||||||
@ -99,21 +99,21 @@ function Screen() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="font-semibold text-sm text-neutral-700 dark:text-neutral-300">
|
<h2 className="text-sm font-semibold text-neutral-700 dark:text-neutral-300">
|
||||||
User Relays (NIP-65)
|
User Relays (NIP-65)
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex flex-col py-2 bg-black/5 dark:bg-white/5 rounded-xl px-3">
|
<div className="flex flex-col px-3 py-2 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
<p className="text-sm text-yellow-500">
|
<p className="text-sm text-yellow-500">
|
||||||
Lume will automatically connect to the user's relay list, but the
|
Lume will automatically connect to the user's relay list, but the
|
||||||
manager function (like adding, removing, changing relay purpose)
|
manager function (like adding, removing, changing relay purpose)
|
||||||
is not yet available.
|
is not yet available.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl px-3">
|
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
{relayList.read?.map((relay) => (
|
{relayList.read?.map((relay) => (
|
||||||
<div
|
<div
|
||||||
key={relay}
|
key={relay}
|
||||||
className="flex justify-between items-center h-11"
|
className="flex items-center justify-between h-11"
|
||||||
>
|
>
|
||||||
<div className="text-sm font-medium">{relay}</div>
|
<div className="text-sm font-medium">{relay}</div>
|
||||||
<div className="text-xs font-semibold">READ</div>
|
<div className="text-xs font-semibold">READ</div>
|
||||||
@ -122,7 +122,7 @@ function Screen() {
|
|||||||
{relayList.write?.map((relay) => (
|
{relayList.write?.map((relay) => (
|
||||||
<div
|
<div
|
||||||
key={relay}
|
key={relay}
|
||||||
className="flex justify-between items-center h-11"
|
className="flex items-center justify-between h-11"
|
||||||
>
|
>
|
||||||
<div className="text-sm font-medium">{relay}</div>
|
<div className="text-sm font-medium">{relay}</div>
|
||||||
<div className="text-xs font-semibold">WRITE</div>
|
<div className="text-xs font-semibold">WRITE</div>
|
||||||
@ -131,7 +131,7 @@ function Screen() {
|
|||||||
{relayList.both?.map((relay) => (
|
{relayList.both?.map((relay) => (
|
||||||
<div
|
<div
|
||||||
key={relay}
|
key={relay}
|
||||||
className="flex justify-between items-center h-11"
|
className="flex items-center justify-between h-11"
|
||||||
>
|
>
|
||||||
<div className="text-sm font-medium">{relay}</div>
|
<div className="text-sm font-medium">{relay}</div>
|
||||||
<div className="text-xs font-semibold">READ + WRITE</div>
|
<div className="text-xs font-semibold">READ + WRITE</div>
|
||||||
|
@ -5,9 +5,9 @@ import type { Metadata } from "@lume/types";
|
|||||||
import { Spinner } from "@lume/ui";
|
import { Spinner } from "@lume/ui";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/settings/user")({
|
export const Route = createFileRoute("/settings/user")({
|
||||||
beforeLoad: async () => {
|
beforeLoad: async () => {
|
||||||
@ -34,31 +34,31 @@ function Screen() {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error(String(e));
|
await message(String(e), { title: "Profile", kind: "error" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full h-full">
|
<div className="flex w-full h-full">
|
||||||
<div className="flex-1 h-full flex items-center flex-col justify-center gap-3">
|
<div className="flex flex-col items-center justify-center flex-1 h-full gap-3">
|
||||||
<div className="relative size-24 rounded-full bg-gradient-to-tr from-orange-100 via-red-50 to-blue-200">
|
<div className="relative rounded-full size-24 bg-gradient-to-tr from-orange-100 via-red-50 to-blue-200">
|
||||||
{profile.picture ? (
|
{profile.picture ? (
|
||||||
<img
|
<img
|
||||||
src={picture || profile.picture}
|
src={picture || profile.picture}
|
||||||
alt="avatar"
|
alt="avatar"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
className="absolute inset-0 z-10 h-full w-full rounded-full object-cover"
|
className="absolute inset-0 z-10 object-cover w-full h-full rounded-full"
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<AvatarUploader
|
<AvatarUploader
|
||||||
setPicture={setPicture}
|
setPicture={setPicture}
|
||||||
className="absolute inset-0 z-20 flex h-full w-full items-center justify-center rounded-full bg-black/10 text-white hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20"
|
className="absolute inset-0 z-20 flex items-center justify-center w-full h-full text-white rounded-full bg-black/10 hover:bg-black/20 dark:bg-white/10 dark:hover:bg-white/20"
|
||||||
>
|
>
|
||||||
<PlusIcon className="size-8" />
|
<PlusIcon className="size-8" />
|
||||||
</AvatarUploader>
|
</AvatarUploader>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center flex flex-col items-center">
|
<div className="flex flex-col items-center text-center">
|
||||||
<div className="text-lg font-semibold">{profile.display_name}</div>
|
<div className="text-lg font-semibold">{profile.display_name}</div>
|
||||||
<div className="text-neutral-800 dark:text-neutral-200">
|
<div className="text-neutral-800 dark:text-neutral-200">
|
||||||
{profile.nip05}
|
{profile.nip05}
|
||||||
@ -66,7 +66,7 @@ function Screen() {
|
|||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Link
|
<Link
|
||||||
to="/settings/backup"
|
to="/settings/backup"
|
||||||
className="px-5 h-9 border border-blue-300 text-sm font-medium hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800 rounded-full bg-blue-100 text-blue-500 inline-flex items-center justify-center"
|
className="inline-flex items-center justify-center px-5 text-sm font-medium text-blue-500 bg-blue-100 border border-blue-300 rounded-full h-9 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800"
|
||||||
>
|
>
|
||||||
Backup Account
|
Backup Account
|
||||||
</Link>
|
</Link>
|
||||||
@ -78,7 +78,7 @@ function Screen() {
|
|||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
className="flex flex-col gap-3 mb-0"
|
className="flex flex-col gap-3 mb-0"
|
||||||
>
|
>
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="display_name"
|
htmlFor="display_name"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -89,10 +89,10 @@ function Screen() {
|
|||||||
name="display_name"
|
name="display_name"
|
||||||
{...register("display_name")}
|
{...register("display_name")}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="name"
|
htmlFor="name"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -103,10 +103,10 @@ function Screen() {
|
|||||||
name="name"
|
name="name"
|
||||||
{...register("name")}
|
{...register("name")}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="website"
|
htmlFor="website"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -118,10 +118,10 @@ function Screen() {
|
|||||||
type="url"
|
type="url"
|
||||||
{...register("website")}
|
{...register("website")}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="banner"
|
htmlFor="banner"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -133,10 +133,10 @@ function Screen() {
|
|||||||
type="url"
|
type="url"
|
||||||
{...register("banner")}
|
{...register("banner")}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="nip05"
|
htmlFor="nip05"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -148,10 +148,10 @@ function Screen() {
|
|||||||
type="email"
|
type="email"
|
||||||
{...register("nip05")}
|
{...register("nip05")}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="lnaddress"
|
htmlFor="lnaddress"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -162,13 +162,13 @@ function Screen() {
|
|||||||
name="lnaddress"
|
name="lnaddress"
|
||||||
type="email"
|
type="email"
|
||||||
{...register("lud16")}
|
{...register("lud16")}
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end">
|
<div className="flex items-center justify-end">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="inline-flex h-9 w-32 px-2 items-center justify-center rounded-lg bg-blue-500 font-medium text-sm text-white hover:bg-blue-600 disabled:opacity-50"
|
className="inline-flex items-center justify-center w-32 px-2 text-sm font-medium text-white bg-blue-500 rounded-lg h-9 hover:bg-blue-600 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{loading ? <Spinner className="size-4" /> : "Update Profile"}
|
{loading ? <Spinner className="size-4" /> : "Update Profile"}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
export const Route = createLazyFileRoute("/settings/zap")({
|
export const Route = createLazyFileRoute("/settings/zap")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
@ -9,7 +9,7 @@ export const Route = createLazyFileRoute("/settings/zap")({
|
|||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-xl">
|
<div className="w-full max-w-xl mx-auto">
|
||||||
<div className="flex flex-col gap-3 divide-y divide-neutral-300 dark:divide-neutral-700">
|
<div className="flex flex-col gap-3 divide-y divide-neutral-300 dark:divide-neutral-700">
|
||||||
<div className="flex flex-col gap-6 py-3">
|
<div className="flex flex-col gap-6 py-3">
|
||||||
<Connection />
|
<Connection />
|
||||||
@ -27,17 +27,17 @@ function Connection() {
|
|||||||
try {
|
try {
|
||||||
await invoke("set_nwc", { uri });
|
await invoke("set_nwc", { uri });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error(String(e));
|
await message(String(e), { title: "Zap", kind: "info" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start gap-6">
|
<div className="flex items-start gap-6">
|
||||||
<div className="w-36 shrink-0 text-end font-medium text-sm">
|
<div className="text-sm font-medium w-36 shrink-0 text-end">
|
||||||
Connection
|
Connection
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="nwc"
|
htmlFor="nwc"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -51,12 +51,12 @@ function Connection() {
|
|||||||
value={uri}
|
value={uri}
|
||||||
onChange={(e) => setUri(e.target.value)}
|
onChange={(e) => setUri(e.target.value)}
|
||||||
placeholder="nostrconnect://"
|
placeholder="nostrconnect://"
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => connect()}
|
onClick={() => connect()}
|
||||||
className="inline-flex h-9 w-24 items-center justify-center rounded-lg bg-neutral-200 text-sm font-medium hover:bg-neutral-300 dark:bg-neutral-900 dark:hover:bg-neutral-700"
|
className="inline-flex items-center justify-center w-24 text-sm font-medium rounded-lg h-9 bg-neutral-200 hover:bg-neutral-300 dark:bg-neutral-900 dark:hover:bg-neutral-700"
|
||||||
>
|
>
|
||||||
Connect
|
Connect
|
||||||
</button>
|
</button>
|
||||||
@ -70,11 +70,11 @@ function Connection() {
|
|||||||
function DefaultAmount() {
|
function DefaultAmount() {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start gap-6">
|
<div className="flex items-start gap-6">
|
||||||
<div className="w-36 shrink-0 text-end font-medium text-sm">
|
<div className="text-sm font-medium w-36 shrink-0 text-end">
|
||||||
Default amount
|
Default amount
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex flex-col w-full gap-1">
|
||||||
<label
|
<label
|
||||||
htmlFor="amount"
|
htmlFor="amount"
|
||||||
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
className="text-sm font-medium text-neutral-700 dark:text-neutral-300"
|
||||||
@ -86,11 +86,11 @@ function DefaultAmount() {
|
|||||||
name="amount"
|
name="amount"
|
||||||
type="number"
|
type="number"
|
||||||
value={21}
|
value={21}
|
||||||
className="h-9 w-full rounded-lg border-neutral-300 bg-transparent px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
className="w-full px-3 bg-transparent rounded-lg h-9 border-neutral-300 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:border-neutral-700 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex h-9 w-24 items-center justify-center rounded-lg bg-neutral-200 text-sm font-medium hover:bg-neutral-300 dark:bg-neutral-900 dark:hover:bg-neutral-700"
|
className="inline-flex items-center justify-center w-24 text-sm font-medium rounded-lg h-9 bg-neutral-200 hover:bg-neutral-300 dark:bg-neutral-900 dark:hover:bg-neutral-700"
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Balance } from "@/components/balance";
|
import { Balance } from "@/components/balance";
|
||||||
import { Box, Container } from "@lume/ui";
|
|
||||||
import { User } from "@/components/user";
|
import { User } from "@/components/user";
|
||||||
|
import { LumeEvent } from "@lume/system";
|
||||||
|
import { Box, Container } from "@lume/ui";
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { getCurrent } from "@tauri-apps/api/webviewWindow";
|
import { getCurrent } from "@tauri-apps/api/webviewWindow";
|
||||||
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import CurrencyInput from "react-currency-input-field";
|
import CurrencyInput from "react-currency-input-field";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
|
||||||
import { LumeEvent } from "@lume/system";
|
|
||||||
|
|
||||||
const DEFAULT_VALUES = [69, 100, 200, 500];
|
const DEFAULT_VALUES = [69, 100, 200, 500];
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ function Screen() {
|
|||||||
const { pubkey, account } = Route.useSearch();
|
const { pubkey, account } = Route.useSearch();
|
||||||
|
|
||||||
const [amount, setAmount] = useState(21);
|
const [amount, setAmount] = useState(21);
|
||||||
const [message, setMessage] = useState("");
|
const [content, setContent] = useState("");
|
||||||
const [isCompleted, setIsCompleted] = useState(false);
|
const [isCompleted, setIsCompleted] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ function Screen() {
|
|||||||
// start loading
|
// start loading
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const val = await LumeEvent.zap(id, amount, message);
|
const val = await LumeEvent.zap(id, amount, content);
|
||||||
|
|
||||||
if (val) {
|
if (val) {
|
||||||
setIsCompleted(true);
|
setIsCompleted(true);
|
||||||
@ -41,7 +41,10 @@ function Screen() {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
toast.error(e);
|
await message(String(e), {
|
||||||
|
title: "Zap",
|
||||||
|
kind: "error",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,19 +52,19 @@ function Screen() {
|
|||||||
<Container>
|
<Container>
|
||||||
<Balance account={account} />
|
<Balance account={account} />
|
||||||
<Box className="flex flex-col gap-3">
|
<Box className="flex flex-col gap-3">
|
||||||
<div className="flex h-full flex-col justify-between py-5">
|
<div className="flex flex-col justify-between h-full py-5">
|
||||||
<div className="flex h-11 shrink-0 items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2 h-11 shrink-0">
|
||||||
{t("note.zap.modalTitle")}{" "}
|
{t("note.zap.modalTitle")}{" "}
|
||||||
<User.Provider pubkey={pubkey}>
|
<User.Provider pubkey={pubkey}>
|
||||||
<User.Root className="inline-flex items-center gap-2 rounded-full bg-neutral-100 p-1 dark:bg-neutral-900">
|
<User.Root className="inline-flex items-center gap-2 p-1 rounded-full bg-neutral-100 dark:bg-neutral-900">
|
||||||
<User.Avatar className="size-6 rounded-full" />
|
<User.Avatar className="rounded-full size-6" />
|
||||||
<User.Name className="pr-2 text-sm font-medium" />
|
<User.Name className="pr-2 text-sm font-medium" />
|
||||||
</User.Root>
|
</User.Root>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-1 flex-col justify-between px-5">
|
<div className="flex flex-col justify-between flex-1 px-5">
|
||||||
<div className="relative flex flex-1 flex-col pb-8">
|
<div className="relative flex flex-col flex-1 pb-8">
|
||||||
<div className="inline-flex h-full flex-1 items-center justify-center gap-1">
|
<div className="inline-flex items-center justify-center flex-1 h-full gap-1">
|
||||||
<CurrencyInput
|
<CurrencyInput
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
defaultValue={21}
|
defaultValue={21}
|
||||||
@ -71,9 +74,9 @@ function Screen() {
|
|||||||
max={10000} // 1M sats
|
max={10000} // 1M sats
|
||||||
maxLength={10000} // 1M sats
|
maxLength={10000} // 1M sats
|
||||||
onValueChange={(value) => setAmount(Number(value))}
|
onValueChange={(value) => setAmount(Number(value))}
|
||||||
className="w-full flex-1 border-none bg-transparent text-right text-4xl font-semibold placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400"
|
className="flex-1 w-full text-4xl font-semibold text-right bg-transparent border-none placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400"
|
||||||
/>
|
/>
|
||||||
<span className="w-full flex-1 text-left text-4xl font-semibold text-neutral-500 dark:text-neutral-400">
|
<span className="flex-1 w-full text-4xl font-semibold text-left text-neutral-500 dark:text-neutral-400">
|
||||||
sats
|
sats
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -90,11 +93,11 @@ function Screen() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-2">
|
<div className="flex flex-col w-full gap-2">
|
||||||
<input
|
<input
|
||||||
name="message"
|
name="message"
|
||||||
value={message}
|
value={content}
|
||||||
onChange={(e) => setMessage(e.target.value)}
|
onChange={(e) => setContent(e.target.value)}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
|
194
pnpm-lock.yaml
generated
194
pnpm-lock.yaml
generated
@ -72,18 +72,6 @@ importers:
|
|||||||
'@radix-ui/react-checkbox':
|
'@radix-ui/react-checkbox':
|
||||||
specifier: ^1.0.4
|
specifier: ^1.0.4
|
||||||
version: 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
version: 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
||||||
'@radix-ui/react-collapsible':
|
|
||||||
specifier: ^1.0.3
|
|
||||||
version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-dialog':
|
|
||||||
specifier: ^1.0.5
|
|
||||||
version: 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-dropdown-menu':
|
|
||||||
specifier: ^2.0.6
|
|
||||||
version: 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-hover-card':
|
|
||||||
specifier: ^1.0.7
|
|
||||||
version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-popover':
|
'@radix-ui/react-popover':
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
||||||
@ -138,9 +126,6 @@ importers:
|
|||||||
react-hook-form:
|
react-hook-form:
|
||||||
specifier: ^7.52.0
|
specifier: ^7.52.0
|
||||||
version: 7.52.0(react@18.3.1)
|
version: 7.52.0(react@18.3.1)
|
||||||
react-hotkeys-hook:
|
|
||||||
specifier: ^4.5.0
|
|
||||||
version: 4.5.0(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
react-i18next:
|
react-i18next:
|
||||||
specifier: ^14.1.2
|
specifier: ^14.1.2
|
||||||
version: 14.1.2(i18next@23.11.5)(react-dom@18.3.1)(react@18.3.1)
|
version: 14.1.2(i18next@23.11.5)(react-dom@18.3.1)(react@18.3.1)
|
||||||
@ -153,9 +138,6 @@ importers:
|
|||||||
slate-react:
|
slate-react:
|
||||||
specifier: ^0.105.0
|
specifier: ^0.105.0
|
||||||
version: 0.105.0(react-dom@18.3.1)(react@18.3.1)(slate@0.103.0)
|
version: 0.105.0(react-dom@18.3.1)(react@18.3.1)(slate@0.103.0)
|
||||||
sonner:
|
|
||||||
specifier: ^1.5.0
|
|
||||||
version: 1.5.0(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
use-debounce:
|
use-debounce:
|
||||||
specifier: ^10.0.1
|
specifier: ^10.0.1
|
||||||
version: 10.0.1(react@18.3.1)
|
version: 10.0.1(react@18.3.1)
|
||||||
@ -1588,34 +1570,6 @@ packages:
|
|||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
|
||||||
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.7
|
|
||||||
'@radix-ui/primitive': 1.0.1
|
|
||||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@types/react': 18.3.3
|
|
||||||
'@types/react-dom': 18.3.0
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@radix-ui/react-collection@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
/@radix-ui/react-collection@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
|
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1694,40 +1648,6 @@ packages:
|
|||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
|
||||||
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.7
|
|
||||||
'@radix-ui/primitive': 1.0.1
|
|
||||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@types/react': 18.3.3
|
|
||||||
'@types/react-dom': 18.3.0
|
|
||||||
aria-hidden: 1.2.4
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@radix-ui/react-direction@1.0.1(@types/react@18.3.3)(react@18.3.1):
|
/@radix-ui/react-direction@1.0.1(@types/react@18.3.3)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
|
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1780,33 +1700,6 @@ packages:
|
|||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
|
||||||
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.7
|
|
||||||
'@radix-ui/primitive': 1.0.1
|
|
||||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-menu': 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@types/react': 18.3.3
|
|
||||||
'@types/react-dom': 18.3.0
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.3)(react@18.3.1):
|
/@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.3)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
|
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1844,35 +1737,6 @@ packages:
|
|||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
|
||||||
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.24.7
|
|
||||||
'@radix-ui/primitive': 1.0.1
|
|
||||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-popper': 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@types/react': 18.3.3
|
|
||||||
'@types/react-dom': 18.3.0
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@radix-ui/react-id@1.0.1(@types/react@18.3.3)(react@18.3.1):
|
/@radix-ui/react-id@1.0.1(@types/react@18.3.3)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
|
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1888,44 +1752,6 @@ packages:
|
|||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-menu@2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
|
||||||
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.7
|
|
||||||
'@radix-ui/primitive': 1.0.1
|
|
||||||
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-popper': 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
|
||||||
'@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
'@types/react': 18.3.3
|
|
||||||
'@types/react-dom': 18.3.0
|
|
||||||
aria-hidden: 1.2.4
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
|
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -5222,16 +5048,6 @@ packages:
|
|||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-hotkeys-hook@4.5.0(react-dom@18.3.1)(react@18.3.1):
|
|
||||||
resolution: {integrity: sha512-Samb85GSgAWFQNvVt3PS90LPPGSf9mkH/r4au81ZP1yOIFayLC3QAvqTgGtJ8YEDMXtPmaVBs6NgipHO6h4Mug==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.1'
|
|
||||||
react-dom: '>=16.8.1'
|
|
||||||
dependencies:
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1)(react@18.3.1):
|
/react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.3.1)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==}
|
resolution: {integrity: sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -5653,16 +5469,6 @@ packages:
|
|||||||
tiny-warning: 1.0.3
|
tiny-warning: 1.0.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/sonner@1.5.0(react-dom@18.3.1)(react@18.3.1):
|
|
||||||
resolution: {integrity: sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^18.0.0
|
|
||||||
react-dom: ^18.0.0
|
|
||||||
dependencies:
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/source-map-js@1.2.0:
|
/source-map-js@1.2.0:
|
||||||
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user