Event Subscriptions (#218)
* feat: improve create column command * refactor: thread * feat: add window virtualized to event screen * chore: update deps * fix: window decoration * feat: improve mention ntoe * feat: add subscription to event screen
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Lume Desktop</title>
|
<title>Lume Desktop</title>
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
class="relative h-screen w-screen cursor-default select-none overflow-hidden font-sans text-black antialiased dark:text-white"
|
class="relative h-screen w-screen cursor-default select-none overflow-hidden font-sans text-black antialiased dark:text-white"
|
||||||
>
|
>
|
||||||
<div id="root" class="h-full w-full"></div>
|
<div id="root" class="h-full w-full"></div>
|
||||||
<script type="module" src="/src/app.tsx"></script>
|
<script type="module" src="/src/app.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -14,16 +14,16 @@
|
|||||||
"@lume/system": "workspace:^",
|
"@lume/system": "workspace:^",
|
||||||
"@lume/ui": "workspace:^",
|
"@lume/ui": "workspace:^",
|
||||||
"@lume/utils": "workspace:^",
|
"@lume/utils": "workspace:^",
|
||||||
"@radix-ui/react-avatar": "^1.0.4",
|
"@radix-ui/react-avatar": "^1.1.0",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.1.0",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
"@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.1.0",
|
||||||
"@radix-ui/react-tabs": "^1.0.4",
|
"@radix-ui/react-tabs": "^1.1.0",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.1.1",
|
||||||
"@tanstack/query-persist-client-core": "^5.45.0",
|
"@tanstack/query-persist-client-core": "^5.48.0",
|
||||||
"@tanstack/react-query": "^5.45.0",
|
"@tanstack/react-query": "^5.48.0",
|
||||||
"@tanstack/react-router": "^1.38.1",
|
"@tanstack/react-router": "^1.40.0",
|
||||||
"embla-carousel-react": "^8.1.5",
|
"embla-carousel-react": "^8.1.5",
|
||||||
"i18next": "^23.11.5",
|
"i18next": "^23.11.5",
|
||||||
"i18next-resources-to-backend": "^1.2.1",
|
"i18next-resources-to-backend": "^1.2.1",
|
||||||
@@ -45,15 +45,15 @@
|
|||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@tanstack/router-devtools": "^1.38.1",
|
"@tanstack/router-devtools": "^1.40.0",
|
||||||
"@tanstack/router-vite-plugin": "^1.38.0",
|
"@tanstack/router-vite-plugin": "^1.39.13",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.7.0",
|
"@vitejs/plugin-react-swc": "^3.7.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.5.2",
|
||||||
"vite": "^5.3.1",
|
"vite": "^5.3.1",
|
||||||
"vite-plugin-top-level-await": "^1.4.1",
|
"vite-plugin-top-level-await": "^1.4.1",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
|
import { type } from "@tauri-apps/plugin-os";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { routeTree } from "./router.gen"; // auto generated file
|
import { routeTree } from "./router.gen"; // auto generated file
|
||||||
import "./app.css";
|
import "./app.css";
|
||||||
|
|
||||||
// Set up a Router instance
|
// Set up a Router instance
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
const platform = type();
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
routeTree,
|
routeTree,
|
||||||
context: { queryClient },
|
context: { queryClient, platform },
|
||||||
Wrap: ({ children }) => {
|
Wrap: ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||||
|
|||||||
@@ -60,15 +60,17 @@ export function Column({
|
|||||||
const rect = container.current.getBoundingClientRect();
|
const rect = container.current.getBoundingClientRect();
|
||||||
const url = `${column.content}?account=${account}&label=${column.label}&name=${column.name}`;
|
const url = `${column.content}?account=${account}&label=${column.label}&name=${column.name}`;
|
||||||
|
|
||||||
// create new webview
|
const prop = {
|
||||||
invoke("create_column", {
|
|
||||||
label: webviewLabel,
|
label: webviewLabel,
|
||||||
x: rect.x,
|
x: rect.x,
|
||||||
y: rect.y,
|
y: rect.y,
|
||||||
width: rect.width,
|
width: rect.width,
|
||||||
height: rect.height,
|
height: rect.height,
|
||||||
url,
|
url,
|
||||||
}).then(() => {
|
};
|
||||||
|
|
||||||
|
// create new webview
|
||||||
|
invoke("create_column", { column: prop }).then(() => {
|
||||||
console.log("created: ", webviewLabel);
|
console.log("created: ", webviewLabel);
|
||||||
setIsCreated(true);
|
setIsCreated(true);
|
||||||
});
|
});
|
||||||
@@ -87,7 +89,7 @@ export function Column({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col w-full h-full rounded-xl",
|
"flex flex-col w-full h-full rounded-xl",
|
||||||
column.label !== "open"
|
column.label !== "open"
|
||||||
? "bg-black/5 dark:bg-white/5 backdrop-blur-sm"
|
? "bg-black/5 dark:bg-white/10 backdrop-blur"
|
||||||
: "",
|
: "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export function NoteReply({ large = false }: { large?: boolean }) {
|
|||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
||||||
large
|
large
|
||||||
? "rounded-full bg-neutral-100 dark:bg-white/10 h-7 gap-1.5 w-24 text-sm font-medium hover:text-blue-500 hover:bg-neutral-200 dark:hover:bg-white/20"
|
? "rounded-full h-7 gap-1.5 w-20 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
: "size-7",
|
: "size-7",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ export function NoteRepost({ large = false }: { large?: boolean }) {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => showContextMenu(e)}
|
onClick={(e) => showContextMenu(e)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200 rounded-full",
|
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
||||||
large
|
large
|
||||||
? "bg-neutral-100 dark:bg-white/10 h-7 gap-1.5 w-24 text-sm font-medium hover:text-blue-500 hover:bg-neutral-200 dark:hover:bg-white/20"
|
? "rounded-full h-7 gap-1.5 w-24 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
: "size-7",
|
: "size-7",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ export function NoteZap({ large = false }: { large?: boolean }) {
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => LumeWindow.openZap(event.id, event.pubkey)}
|
onClick={() => LumeWindow.openZap(event.id)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
|
||||||
large
|
large
|
||||||
? "rounded-full bg-neutral-100 dark:bg-white/10 h-7 gap-1.5 w-24 text-sm font-medium hover:text-blue-500 hover:bg-neutral-200 dark:hover:bg-white/20"
|
? "rounded-full h-7 gap-1.5 w-20 text-sm font-medium hover:bg-black/10 dark:hover:bg-white/10"
|
||||||
: "size-7",
|
: "size-7",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -15,60 +15,63 @@ export function MentionNote({
|
|||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center w-full h-20 mt-2 border rounded-xl border-black/10 dark:border-white/10">
|
<div className="py-2">
|
||||||
<Spinner className="size-5" />
|
<div className="pl-4 py-3 flex flex-col w-full border-l-2 border-black/5 dark:border-white/5">
|
||||||
|
<Spinner className="size-5" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError || !data) {
|
if (isError || !data) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full p-3 mt-2 border rounded-xl border-black/10 dark:border-white/10">
|
<div className="py-2">
|
||||||
Event not found with your current relay set
|
<div className="pl-4 py-3 flex flex-col w-full border-l-2 border-black/5 dark:border-white/5">
|
||||||
|
<p className="text-sm font-medium text-red-500">
|
||||||
|
Event not found with your current relay set
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-full border rounded-lg cursor-default border-black/10 dark:border-white/10">
|
<div className="py-2">
|
||||||
<User.Provider pubkey={data.pubkey}>
|
<div className="pl-4 py-3 flex flex-col w-full border-l-2 border-black/5 dark:border-white/5">
|
||||||
<User.Root className="flex items-center gap-2 px-3 h-11">
|
<User.Provider pubkey={data.pubkey}>
|
||||||
<User.Avatar className="object-cover rounded-full size-6 shrink-0" />
|
<User.Root className="flex items-center gap-2 h-8">
|
||||||
<div className="inline-flex items-center flex-1 gap-2">
|
<User.Avatar className="object-cover rounded-full size-6 shrink-0" />
|
||||||
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
|
<div className="inline-flex items-center flex-1 gap-2">
|
||||||
<span className="text-neutral-600 dark:text-neutral-400">·</span>
|
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
|
||||||
<User.Time
|
<span className="text-neutral-600 dark:text-neutral-400">·</span>
|
||||||
time={data.created_at}
|
<User.Time
|
||||||
className="text-neutral-600 dark:text-neutral-400"
|
time={data.created_at}
|
||||||
/>
|
className="text-neutral-600 dark:text-neutral-400"
|
||||||
</div>
|
/>
|
||||||
</User.Root>
|
</div>
|
||||||
</User.Provider>
|
</User.Root>
|
||||||
<div
|
</User.Provider>
|
||||||
className={cn(
|
<div className="select-text text-pretty line-clamp-3 content-break leading-normal">
|
||||||
"px-3 select-text whitespace-normal text-pretty content-break leading-normal",
|
{data.content}
|
||||||
data.content.length > 400 ? "max-h-[150px] gradient-mask-b-0" : "",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{data.content}
|
|
||||||
</div>
|
|
||||||
{openable ? (
|
|
||||||
<div className="flex items-center justify-end px-2 h-11">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
LumeWindow.openEvent(data);
|
|
||||||
}}
|
|
||||||
className="z-10 inline-flex items-center justify-center gap-1 text-sm rounded-full h-7 w-28 bg-black/10 dark:bg-white/10 text-neutral-600 hover:text-blue-500 dark:text-neutral-400"
|
|
||||||
>
|
|
||||||
View post
|
|
||||||
<LinkIcon className="size-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
{openable ? (
|
||||||
<div className="h-3" />
|
<div className="flex items-center justify-start mt-3">
|
||||||
)}
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
LumeWindow.openEvent(data);
|
||||||
|
}}
|
||||||
|
className="inline-flex items-center gap-1 text-blue-500 text-sm"
|
||||||
|
>
|
||||||
|
View post
|
||||||
|
<LinkIcon className="size-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="h-3" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function ImagePreview({ url }: { url: string }) {
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
style={{ contentVisibility: "auto" }}
|
style={{ contentVisibility: "auto" }}
|
||||||
className="max-h-[600px] w-auto object-cover rounded-lg outline outline-1 -outline-offset-1 outline-black/15"
|
className="max-h-[400px] max-w-[400px] h-auto w-auto object-cover rounded-lg outline outline-1 -outline-offset-1 outline-black/15"
|
||||||
onClick={() => open(url)}
|
onClick={() => open(url)}
|
||||||
onKeyDown={() => open(url)}
|
onKeyDown={() => open(url)}
|
||||||
onError={({ currentTarget }) => {
|
onError={({ currentTarget }) => {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export function Images({ urls }: { urls: string[] }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative pl-2 overflow-hidden group">
|
<div className="relative pl-2 overflow-hidden group">
|
||||||
<div ref={emblaRef} className="w-full">
|
<div ref={emblaRef} className="w-full h-[320px]">
|
||||||
<div className="flex w-full gap-2 scrollbar-none">
|
<div className="flex w-full gap-2 scrollbar-none">
|
||||||
{imageUrls.map((url, index) => (
|
{imageUrls.map((url, index) => (
|
||||||
<LazyImage
|
<LazyImage
|
||||||
@@ -109,10 +109,7 @@ export function Images({ urls }: { urls: string[] }) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="absolute z-10 items-center justify-between hidden w-full px-5 transform -translate-x-1/2 -translate-y-1/2 group-hover:flex left-1/2 top-1/2">
|
||||||
aria-hidden
|
|
||||||
className="absolute z-10 items-center justify-between hidden w-full px-5 transform -translate-x-1/2 -translate-y-1/2 group-hover:flex left-1/2 top-1/2"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!emblaApi?.canScrollPrev}
|
disabled={!emblaApi?.canScrollPrev}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export function Videos({ urls }: { urls: string[] }) {
|
|||||||
<div className="group px-3">
|
<div className="group px-3">
|
||||||
<video
|
<video
|
||||||
className="w-full h-auto object-cover rounded-lg outline outline-1 -outline-offset-1 outline-black/15"
|
className="w-full h-auto object-cover rounded-lg outline outline-1 -outline-offset-1 outline-black/15"
|
||||||
|
preload="metadata"
|
||||||
controls
|
controls
|
||||||
muted
|
muted
|
||||||
>
|
>
|
||||||
@@ -23,6 +24,7 @@ export function Videos({ urls }: { urls: string[] }) {
|
|||||||
<CarouselItem key={item} isSnapPoint={isSnapPoint}>
|
<CarouselItem key={item} isSnapPoint={isSnapPoint}>
|
||||||
<video
|
<video
|
||||||
className="w-full h-full object-cover rounded-lg outline outline-1 -outline-offset-1 outline-black/15"
|
className="w-full h-full object-cover rounded-lg outline outline-1 -outline-offset-1 outline-black/15"
|
||||||
|
preload="metadata"
|
||||||
controls={false}
|
controls={false}
|
||||||
muted
|
muted
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function TextNote({
|
|||||||
<Note.Provider event={event}>
|
<Note.Provider event={event}>
|
||||||
<Note.Root
|
<Note.Root
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-white dark:bg-black/20 backdrop-blur-lg rounded-xl shadow-primary dark:ring-1 ring-neutral-800/50",
|
"bg-white dark:bg-black/20 backdrop-blur rounded-xl shadow-primary dark:ring-1 dark:ring-white/5",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -8,30 +8,22 @@ import { Link } from "@tanstack/react-router";
|
|||||||
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
import { Menu, MenuItem } from "@tauri-apps/api/menu";
|
||||||
import { getCurrent } from "@tauri-apps/api/window";
|
import { getCurrent } from "@tauri-apps/api/window";
|
||||||
import { message } from "@tauri-apps/plugin-dialog";
|
import { message } from "@tauri-apps/plugin-dialog";
|
||||||
import { type } from "@tauri-apps/plugin-os";
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
export const Route = createFileRoute("/$account")({
|
export const Route = createFileRoute("/$account")({
|
||||||
beforeLoad: async () => {
|
beforeLoad: async () => {
|
||||||
const accounts = await NostrAccount.getAccounts();
|
const accounts = await NostrAccount.getAccounts();
|
||||||
const os = await type();
|
return { accounts };
|
||||||
|
|
||||||
return { accounts, os };
|
|
||||||
},
|
},
|
||||||
component: Screen,
|
component: Screen,
|
||||||
});
|
});
|
||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
const { os } = Route.useRouteContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-screen h-screen">
|
<div className="flex flex-col w-screen h-screen">
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className={cn(
|
className="flex h-11 shrink-0 items-center justify-between pr-2 ml-2 pl-20"
|
||||||
"flex h-11 shrink-0 items-center justify-between pr-2",
|
|
||||||
os === "macos" ? "ml-2 pl-20" : "pl-4",
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Accounts />
|
<Accounts />
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
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 { OsType } from "@tauri-apps/plugin-os";
|
||||||
|
|
||||||
interface RouterContext {
|
interface RouterContext {
|
||||||
queryClient: QueryClient;
|
queryClient: QueryClient;
|
||||||
|
platform: OsType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Route = createRootRouteWithContext<RouterContext>()({
|
export const Route = createRootRouteWithContext<RouterContext>()({
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
import { Note } from "@/components/note";
|
|
||||||
import { type LumeEvent, NostrQuery } from "@lume/system";
|
|
||||||
import { Box, Container, Spinner } from "@lume/ui";
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { WindowVirtualizer } from "virtua";
|
|
||||||
import { Reply } from "./-components/reply";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/events/$eventId")({
|
|
||||||
beforeLoad: async () => {
|
|
||||||
const settings = await NostrQuery.getUserSettings();
|
|
||||||
return { settings };
|
|
||||||
},
|
|
||||||
loader: async ({ params }) => {
|
|
||||||
const event = await NostrQuery.getEvent(params.eventId);
|
|
||||||
return event;
|
|
||||||
},
|
|
||||||
component: Screen,
|
|
||||||
});
|
|
||||||
|
|
||||||
function Screen() {
|
|
||||||
const event = Route.useLoaderData();
|
|
||||||
|
|
||||||
const [reload, setReload] = useState(false);
|
|
||||||
const [replies, setReplies] = useState<LumeEvent[]>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let mounted = true;
|
|
||||||
|
|
||||||
if (event) {
|
|
||||||
event.getAllReplies().then((data) => {
|
|
||||||
if (mounted) setReplies(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
mounted = false;
|
|
||||||
};
|
|
||||||
}, [event]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container withDrag>
|
|
||||||
<Box className="scrollbar-none">
|
|
||||||
<WindowVirtualizer>
|
|
||||||
<Note.Provider event={event}>
|
|
||||||
<Note.Root>
|
|
||||||
<div className="flex items-center justify-between px-3 h-14">
|
|
||||||
<Note.User />
|
|
||||||
<Note.Menu />
|
|
||||||
</div>
|
|
||||||
<Note.ContentLarge className="px-3" />
|
|
||||||
<div className="flex items-center justify-end gap-2 px-3 mt-4 h-11">
|
|
||||||
<Note.Reply large />
|
|
||||||
<Note.Repost large />
|
|
||||||
<Note.Zap large />
|
|
||||||
</div>
|
|
||||||
</Note.Root>
|
|
||||||
</Note.Provider>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="flex items-center px-3 text-sm font-semibold border-t h-11 text-neutral-700 dark:text-neutral-300 border-neutral-100 dark:border-neutral-900">
|
|
||||||
Replies ({replies?.length ?? 0})
|
|
||||||
</div>
|
|
||||||
{!replies ? (
|
|
||||||
<Spinner />
|
|
||||||
) : !replies.length ? (
|
|
||||||
<div className="flex items-center justify-center w-full">
|
|
||||||
<div className="flex flex-col items-center justify-center gap-2 py-6">
|
|
||||||
<h3 className="text-3xl">👋</h3>
|
|
||||||
<p className="leading-none text-neutral-600 dark:text-neutral-400">
|
|
||||||
Be the first to Reply!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
replies.map((event) => <Reply key={event.id} event={event} />)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</WindowVirtualizer>
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
143
apps/desktop2/src/routes/events/$id.tsx
Normal file
143
apps/desktop2/src/routes/events/$id.tsx
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import { Note } from "@/components/note";
|
||||||
|
import { LumeEvent, NostrQuery } from "@lume/system";
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { Virtualizer } from "virtua";
|
||||||
|
import NoteParent from "./-components/parent";
|
||||||
|
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { getCurrent } from "@tauri-apps/api/window";
|
||||||
|
import type { Meta } from "@lume/types";
|
||||||
|
|
||||||
|
type Payload = {
|
||||||
|
raw: string;
|
||||||
|
parsed: Meta;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/events/$id")({
|
||||||
|
beforeLoad: async () => {
|
||||||
|
const settings = await NostrQuery.getUserSettings();
|
||||||
|
return { settings };
|
||||||
|
},
|
||||||
|
loader: async ({ params }) => {
|
||||||
|
const event = await NostrQuery.getEvent(params.id);
|
||||||
|
return event;
|
||||||
|
},
|
||||||
|
component: Screen,
|
||||||
|
});
|
||||||
|
|
||||||
|
function Screen() {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div
|
||||||
|
data-tauri-drag-region
|
||||||
|
className="shrink-0 h-8 w-full border-b border-black/5 dark:border-white/5"
|
||||||
|
/>
|
||||||
|
<ScrollArea.Root
|
||||||
|
type={"scroll"}
|
||||||
|
scrollHideDelay={300}
|
||||||
|
className="overflow-hidden size-full flex-1"
|
||||||
|
>
|
||||||
|
<ScrollArea.Viewport ref={ref} className="h-full p-3">
|
||||||
|
<RootEvent />
|
||||||
|
<Virtualizer scrollRef={ref}>
|
||||||
|
<ReplyList />
|
||||||
|
</Virtualizer>
|
||||||
|
</ScrollArea.Viewport>
|
||||||
|
<ScrollArea.Scrollbar
|
||||||
|
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
||||||
|
orientation="vertical"
|
||||||
|
>
|
||||||
|
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
||||||
|
</ScrollArea.Scrollbar>
|
||||||
|
<ScrollArea.Corner className="bg-transparent" />
|
||||||
|
</ScrollArea.Root>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RootEvent() {
|
||||||
|
const event = Route.useLoaderData();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Note.Provider event={event}>
|
||||||
|
<Note.Root className="bg-white dark:bg-black/10 backdrop-blur rounded-xl shadow-primary dark:ring-1 dark:ring-white/5">
|
||||||
|
<div className="flex items-center justify-between px-3 h-14">
|
||||||
|
<Note.User />
|
||||||
|
<Note.Menu />
|
||||||
|
</div>
|
||||||
|
<Note.ContentLarge className="px-3" />
|
||||||
|
<div className="flex items-center gap-2 px-3 mt-6 h-12 rounded-b-xl bg-neutral-50 dark:bg-white/5">
|
||||||
|
<Note.Reply large />
|
||||||
|
<Note.Repost large />
|
||||||
|
<Note.Zap large />
|
||||||
|
</div>
|
||||||
|
</Note.Root>
|
||||||
|
</Note.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReplyList() {
|
||||||
|
const event = Route.useLoaderData();
|
||||||
|
const [replies, setReplies] = useState<LumeEvent[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unlistenEvent = getCurrent().listen<Payload>("new_reply", (data) => {
|
||||||
|
const event = LumeEvent.from(data.payload.raw, data.payload.parsed);
|
||||||
|
setReplies((prev) => [event, ...prev]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unlistenWindow = getCurrent().onCloseRequested(async () => {
|
||||||
|
await event.unlistenEventReply();
|
||||||
|
await getCurrent().destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unlistenEvent.then((f) => f());
|
||||||
|
unlistenWindow.then((f) => f());
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let mounted = true;
|
||||||
|
|
||||||
|
async function getReplies() {
|
||||||
|
const data = await event.getEventReplies();
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setReplies(data);
|
||||||
|
// Start listen for new reply
|
||||||
|
event.listenEventReply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getReplies();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center text-sm font-semibold h-14 text-neutral-600 dark:text-white/30">
|
||||||
|
All replies
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
{!replies.length ? (
|
||||||
|
<div className="flex items-center justify-center w-full">
|
||||||
|
<div className="flex flex-col items-center justify-center gap-2 py-4">
|
||||||
|
<h3 className="text-3xl">👋</h3>
|
||||||
|
<p className="leading-none text-neutral-600 dark:text-neutral-400">
|
||||||
|
Be the first to Reply!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
replies.map((event) => <NoteParent key={event.id} event={event} />)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
apps/desktop2/src/routes/events/-components/child.tsx
Normal file
41
apps/desktop2/src/routes/events/-components/child.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Note } from "@/components/note";
|
||||||
|
import type { LumeEvent } from "@lume/system";
|
||||||
|
import NoteParent from "./parent";
|
||||||
|
import { memo } from "react";
|
||||||
|
|
||||||
|
const NoteChild = memo(function NoteChild({ event }: { event: LumeEvent }) {
|
||||||
|
return (
|
||||||
|
<Note.Provider event={event}>
|
||||||
|
<Note.Root className="flex flex-col gap-6 mb-3">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Note.User />
|
||||||
|
<Note.Menu />
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="w-8 shrink-0" />
|
||||||
|
<div className="flex-1 flex flex-col gap-2">
|
||||||
|
<Note.ContentLarge />
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Note.Reply />
|
||||||
|
<Note.Repost />
|
||||||
|
<Note.Zap />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{event.replies?.length ? (
|
||||||
|
<div className="flex flex-col gap-3 pl-4">
|
||||||
|
<div className="flex flex-col pl-6 border-l border-black/10 dark:border-white/10">
|
||||||
|
{event.replies?.map((childEvent) => (
|
||||||
|
<NoteParent key={childEvent.id} event={childEvent} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Note.Root>
|
||||||
|
</Note.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default NoteChild;
|
||||||
41
apps/desktop2/src/routes/events/-components/parent.tsx
Normal file
41
apps/desktop2/src/routes/events/-components/parent.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Note } from "@/components/note";
|
||||||
|
import type { LumeEvent } from "@lume/system";
|
||||||
|
import NoteChild from "./child";
|
||||||
|
import { memo } from "react";
|
||||||
|
|
||||||
|
const NoteParent = memo(function NoteParent({ event }: { event: LumeEvent }) {
|
||||||
|
return (
|
||||||
|
<Note.Provider event={event}>
|
||||||
|
<Note.Root className="flex flex-col gap-6 mb-3">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Note.User />
|
||||||
|
<Note.Menu />
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="w-8 shrink-0" />
|
||||||
|
<div className="flex-1 flex flex-col gap-2">
|
||||||
|
<Note.ContentLarge />
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Note.Reply />
|
||||||
|
<Note.Repost />
|
||||||
|
<Note.Zap />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{event.replies?.length ? (
|
||||||
|
<div className="flex flex-col gap-3 pl-4">
|
||||||
|
<div className="flex flex-col gap-3 pl-6 border-l border-black/10 dark:border-white/10">
|
||||||
|
{event.replies?.map((childEvent) => (
|
||||||
|
<NoteChild key={childEvent.id} event={childEvent} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Note.Root>
|
||||||
|
</Note.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default NoteParent;
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { Note } from "@/components/note";
|
|
||||||
import type { LumeEvent } from "@lume/system";
|
|
||||||
import { cn } from "@lume/utils";
|
|
||||||
import { SubReply } from "./subReply";
|
|
||||||
|
|
||||||
export function Reply({ event }: { event: LumeEvent }) {
|
|
||||||
return (
|
|
||||||
<Note.Provider event={event}>
|
|
||||||
<Note.Root className="border-t border-neutral-100 dark:border-neutral-900">
|
|
||||||
<div className="flex items-center justify-between px-3 h-14">
|
|
||||||
<Note.User />
|
|
||||||
<Note.Menu />
|
|
||||||
</div>
|
|
||||||
<Note.ContentLarge className="px-3" />
|
|
||||||
<div className="flex items-center gap-4 px-3 mt-3 h-14">
|
|
||||||
<Note.Reply />
|
|
||||||
<Note.Repost />
|
|
||||||
<Note.Zap />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
event.replies?.length > 0
|
|
||||||
? "py-2 pl-3 flex flex-col gap-3 divide-y divide-neutral-100 bg-neutral-50 dark:bg-white/5 border-l-2 border-blue-500 dark:divide-neutral-900"
|
|
||||||
: "",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{event.replies?.length > 0
|
|
||||||
? event.replies?.map((childEvent) => (
|
|
||||||
<SubReply key={childEvent.id} event={childEvent} />
|
|
||||||
))
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
</Note.Root>
|
|
||||||
</Note.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import type { NostrEvent } from "@lume/types";
|
|
||||||
import { Note } from "@/components/note";
|
|
||||||
|
|
||||||
export function SubReply({
|
|
||||||
event,
|
|
||||||
}: {
|
|
||||||
event: NostrEvent;
|
|
||||||
rootEventId?: string;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Note.Provider event={event}>
|
|
||||||
<Note.Root>
|
|
||||||
<div className="px-3 h-14 flex items-center justify-between">
|
|
||||||
<Note.User />
|
|
||||||
<Note.Menu />
|
|
||||||
</div>
|
|
||||||
<Note.ContentLarge className="px-3" />
|
|
||||||
<div className="mt-3 flex items-center gap-4 px-3">
|
|
||||||
<Note.Reply />
|
|
||||||
<Note.Repost />
|
|
||||||
<Note.Zap />
|
|
||||||
</div>
|
|
||||||
</Note.Root>
|
|
||||||
</Note.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -67,8 +67,12 @@ function Screen() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className="flex flex-col items-center justify-between w-full h-full"
|
className="relative flex flex-col items-center justify-between w-full h-full"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
data-tauri-drag-region
|
||||||
|
className="absolute top-0 left-0 h-14 w-full"
|
||||||
|
/>
|
||||||
<div className="flex items-end justify-center flex-1 w-full px-4 pb-10">
|
<div className="flex items-end justify-center flex-1 w-full px-4 pb-10">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h2 className="mb-1 text-lg text-neutral-700 dark:text-neutral-300">
|
<h2 className="mb-1 text-lg text-neutral-700 dark:text-neutral-300">
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
"@astrojs/check": "^0.5.10",
|
"@astrojs/check": "^0.5.10",
|
||||||
"@astrojs/tailwind": "^5.1.0",
|
"@astrojs/tailwind": "^5.1.0",
|
||||||
"@fontsource/alice": "^5.0.13",
|
"@fontsource/alice": "^5.0.13",
|
||||||
"astro": "^4.10.2",
|
"astro": "^4.11.1",
|
||||||
"astro-seo-meta": "^4.1.1",
|
"astro-seo-meta": "^4.1.1",
|
||||||
"astro-seo-schema": "^4.0.2",
|
"astro-seo-schema": "^4.0.2",
|
||||||
"schema-dts": "^1.1.2",
|
"schema-dts": "^1.1.2",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.13"
|
"@tailwindcss/typography": "^0.5.13"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.8.1",
|
"@biomejs/biome": "^1.8.2",
|
||||||
"@tauri-apps/cli": "2.0.0-beta.20",
|
"@tauri-apps/cli": "2.0.0-beta.20",
|
||||||
"turbo": "^1.13.4"
|
"turbo": "^1.13.4"
|
||||||
},
|
},
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"@tauri-apps/plugin-fs": "2.0.0-beta.5",
|
"@tauri-apps/plugin-fs": "2.0.0-beta.5",
|
||||||
"@tauri-apps/plugin-http": "2.0.0-beta.5",
|
"@tauri-apps/plugin-http": "2.0.0-beta.5",
|
||||||
"@tauri-apps/plugin-notification": "2.0.0-beta.5",
|
"@tauri-apps/plugin-notification": "2.0.0-beta.5",
|
||||||
"@tauri-apps/plugin-os": "2.0.0-beta.5",
|
"@tauri-apps/plugin-os": "github:tauri-apps/tauri-plugin-os#v2",
|
||||||
"@tauri-apps/plugin-process": "2.0.0-beta.5",
|
"@tauri-apps/plugin-process": "2.0.0-beta.5",
|
||||||
"@tauri-apps/plugin-shell": "2.0.0-beta.6",
|
"@tauri-apps/plugin-shell": "2.0.0-beta.6",
|
||||||
"@tauri-apps/plugin-updater": "2.0.0-beta.5",
|
"@tauri-apps/plugin-updater": "2.0.0-beta.5",
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tsconfig": "workspace:*",
|
"@lume/tsconfig": "workspace:*",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
"main": "./src/index.ts",
|
"main": "./src/index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lume/utils": "workspace:^",
|
"@lume/utils": "workspace:^",
|
||||||
"@tanstack/query-persist-client-core": "^5.45.0",
|
"@tanstack/query-persist-client-core": "^5.48.0",
|
||||||
"@tanstack/react-query": "^5.45.0",
|
"@tanstack/react-query": "^5.48.0",
|
||||||
"nostr-tools": "^2.7.0",
|
"nostr-tools": "^2.7.0",
|
||||||
"react": "^18.3.1"
|
"react": "^18.3.1"
|
||||||
},
|
},
|
||||||
@@ -14,6 +14,6 @@
|
|||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,4 @@
|
|||||||
import type {
|
import type { EventTag, Kind, Meta, NostrEvent } from "@lume/types";
|
||||||
EventTag,
|
|
||||||
EventWithReplies,
|
|
||||||
Kind,
|
|
||||||
Meta,
|
|
||||||
NostrEvent,
|
|
||||||
} from "@lume/types";
|
|
||||||
import { type Result, commands } from "./commands";
|
import { type Result, commands } from "./commands";
|
||||||
|
|
||||||
export class LumeEvent {
|
export class LumeEvent {
|
||||||
@@ -110,52 +104,82 @@ export class LumeEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAllReplies() {
|
public async getEventReplies() {
|
||||||
const query = await commands.getReplies(this.id);
|
const query = await commands.getReplies(this.id);
|
||||||
|
|
||||||
if (query.status === "ok") {
|
if (query.status === "ok") {
|
||||||
const events = query.data.map((item) => {
|
const events = query.data
|
||||||
const nostrEvent: NostrEvent = JSON.parse(item.raw);
|
// Create Lume Events
|
||||||
|
.map((item) => LumeEvent.from(item.raw, item.parsed))
|
||||||
|
// Filter quote event
|
||||||
|
.filter(
|
||||||
|
(ev) =>
|
||||||
|
!ev.tags.filter((t) => t[0] === "q" || t[3] === "mention").length,
|
||||||
|
);
|
||||||
|
|
||||||
if (item.parsed) {
|
if (events.length > 1) {
|
||||||
nostrEvent.meta = item.parsed;
|
const removeQueues = new Set();
|
||||||
} else {
|
|
||||||
nostrEvent.meta = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lumeEvent = new LumeEvent(nostrEvent);
|
|
||||||
|
|
||||||
return lumeEvent;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (events.length > 0) {
|
|
||||||
const replies = new Set();
|
|
||||||
|
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
const tags = event.tags.filter(
|
const tags = event.tags.filter(
|
||||||
(el) => el[0] === "e" && el[1] !== this.id && el[3] !== "mention",
|
(t) => t[0] === "e" && t[1] !== this.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (tags.length > 0) {
|
if (tags.length === 1) {
|
||||||
for (const tag of tags) {
|
const index = events.findIndex((ev) => ev.id === tags[0][1]);
|
||||||
const rootIndex = events.findIndex((el) => el.id === tag[1]);
|
|
||||||
|
|
||||||
if (rootIndex !== -1) {
|
if (index !== -1) {
|
||||||
const rootEvent = events[rootIndex];
|
const rootEvent = events[index];
|
||||||
|
|
||||||
if (rootEvent?.replies) {
|
if (rootEvent.replies?.length) {
|
||||||
rootEvent.replies.push(event);
|
rootEvent.replies.push(event);
|
||||||
} else {
|
} else {
|
||||||
rootEvent.replies = [event];
|
rootEvent.replies = [event];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current event to queue
|
||||||
|
removeQueues.add(event.id);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tag of tags) {
|
||||||
|
const id = tag[1];
|
||||||
|
const rootIndex = events.findIndex((ev) => ev.id === id);
|
||||||
|
|
||||||
|
if (rootIndex !== -1) {
|
||||||
|
const rootEvent = events[rootIndex];
|
||||||
|
|
||||||
|
if (rootEvent.replies?.length) {
|
||||||
|
const childIndex = rootEvent.replies.findIndex(
|
||||||
|
(ev) => ev.id === id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (childIndex !== -1) {
|
||||||
|
const childEvent = rootEvent.replies[rootIndex];
|
||||||
|
|
||||||
|
if (childEvent.replies?.length) {
|
||||||
|
childEvent.replies.push(event);
|
||||||
|
} else {
|
||||||
|
childEvent.replies = [event];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current event to queue
|
||||||
|
removeQueues.add(event.id);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
replies.add(event.id);
|
rootEvent.replies = [event];
|
||||||
|
// Add current event to queue
|
||||||
|
removeQueues.add(event.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return events.filter((ev) => !replies.has(ev.id));
|
return events.filter((ev) => !removeQueues.has(ev.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
return events;
|
return events;
|
||||||
@@ -165,6 +189,26 @@ export class LumeEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async listenEventReply() {
|
||||||
|
const query = await commands.listenEventReply(this.id);
|
||||||
|
|
||||||
|
if (query.status === "ok") {
|
||||||
|
return query.data;
|
||||||
|
} else {
|
||||||
|
throw new Error(query.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async unlistenEventReply() {
|
||||||
|
const query = await commands.unlistenEventReply(this.id);
|
||||||
|
|
||||||
|
if (query.status === "ok") {
|
||||||
|
return query.data;
|
||||||
|
} else {
|
||||||
|
throw new Error(query.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async zap(amount: number, message: string) {
|
public async zap(amount: number, message: string) {
|
||||||
const query = await commands.zapEvent(this.id, amount.toString(), message);
|
const query = await commands.zapEvent(this.id, amount.toString(), message);
|
||||||
|
|
||||||
@@ -226,4 +270,16 @@ export class LumeEvent {
|
|||||||
throw new Error(query.error);
|
throw new Error(query.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static from(raw: string, parsed?: Meta) {
|
||||||
|
const nostrEvent: NostrEvent = JSON.parse(raw);
|
||||||
|
|
||||||
|
if (parsed) {
|
||||||
|
nostrEvent.meta = parsed;
|
||||||
|
} else {
|
||||||
|
nostrEvent.meta = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new this(nostrEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ export class LumeWindow {
|
|||||||
const reply: string =
|
const reply: string =
|
||||||
eTags.find((el) => el[3] === "reply")?.[1] ?? eTags[1]?.[1];
|
eTags.find((el) => el[3] === "reply")?.[1] ?? eTags[1]?.[1];
|
||||||
|
|
||||||
const label = `event-${event.id}`;
|
|
||||||
const url = `/events/${root ?? reply ?? event.id}`;
|
const url = `/events/${root ?? reply ?? event.id}`;
|
||||||
|
const label = `event-${root ?? reply ?? event.id}`;
|
||||||
|
|
||||||
const query = await commands.openWindow({
|
const query = await commands.openWindow({
|
||||||
label,
|
label,
|
||||||
@@ -26,6 +26,7 @@ export class LumeWindow {
|
|||||||
height: 800,
|
height: 800,
|
||||||
maximizable: true,
|
maximizable: true,
|
||||||
minimizable: true,
|
minimizable: true,
|
||||||
|
hidden_title: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (query.status === "ok") {
|
if (query.status === "ok") {
|
||||||
@@ -45,6 +46,7 @@ export class LumeWindow {
|
|||||||
height: 800,
|
height: 800,
|
||||||
maximizable: true,
|
maximizable: true,
|
||||||
minimizable: true,
|
minimizable: true,
|
||||||
|
hidden_title: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (query.status === "ok") {
|
if (query.status === "ok") {
|
||||||
@@ -76,8 +78,9 @@ export class LumeWindow {
|
|||||||
title: "Editor",
|
title: "Editor",
|
||||||
width: 560,
|
width: 560,
|
||||||
height: 340,
|
height: 340,
|
||||||
maximizable: true,
|
maximizable: false,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
|
hidden_title: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (query.status === "ok") {
|
if (query.status === "ok") {
|
||||||
@@ -99,6 +102,7 @@ export class LumeWindow {
|
|||||||
height: 460,
|
height: 460,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
|
hidden_title: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await LumeWindow.openSettings("bitcoin-connect");
|
await LumeWindow.openSettings("bitcoin-connect");
|
||||||
@@ -115,6 +119,7 @@ export class LumeWindow {
|
|||||||
height: 500,
|
height: 500,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
|
hidden_title: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (query.status === "ok") {
|
if (query.status === "ok") {
|
||||||
@@ -134,6 +139,7 @@ export class LumeWindow {
|
|||||||
height: 600,
|
height: 600,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
|
hidden_title: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (query.status === "ok") {
|
if (query.status === "ok") {
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,6 @@
|
|||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,6 @@
|
|||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"tailwind-merge": "^2.3.0",
|
"tailwind-merge": "^2.3.0",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1263
pnpm-lock.yaml
generated
1263
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
553
src-tauri/Cargo.lock
generated
553
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,7 @@
|
|||||||
"window:allow-start-dragging",
|
"window:allow-start-dragging",
|
||||||
"window:allow-create",
|
"window:allow-create",
|
||||||
"window:allow-close",
|
"window:allow-close",
|
||||||
|
"window:allow-destroy",
|
||||||
"window:allow-set-focus",
|
"window:allow-set-focus",
|
||||||
"window:allow-center",
|
"window:allow-center",
|
||||||
"window:allow-minimize",
|
"window:allow-minimize",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"desktop-capability":{"identifier":"desktop-capability","description":"Capability for the desktop","local":true,"windows":["main","panel","splash","settings","search","nwc","activity","zap-*","event-*","user-*","editor-*","column-*"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","window:allow-start-dragging","window:allow-create","window:allow-close","window:allow-set-focus","window:allow-center","window:allow-minimize","window:allow-maximize","window:allow-set-size","window:allow-set-focus","window:allow-start-dragging","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","webview:allow-create-webview-window","webview:allow-create-webview","webview:allow-set-webview-size","webview:allow-set-webview-position","webview:allow-webview-close","dialog:allow-open","dialog:allow-ask","dialog:allow-message","process:allow-restart","fs:allow-read-file","theme:allow-set-theme","theme:allow-get-theme","menu:allow-new","menu:allow-popup","http:default","shell:allow-open",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]}}
|
{"desktop-capability":{"identifier":"desktop-capability","description":"Capability for the desktop","local":true,"windows":["main","panel","splash","settings","search","nwc","activity","zap-*","event-*","user-*","editor-*","column-*"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","window:allow-start-dragging","window:allow-create","window:allow-close","window:allow-destroy","window:allow-set-focus","window:allow-center","window:allow-minimize","window:allow-maximize","window:allow-set-size","window:allow-set-focus","window:allow-start-dragging","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","webview:allow-create-webview-window","webview:allow-create-webview","webview:allow-set-webview-size","webview:allow-set-webview-position","webview:allow-webview-close","dialog:allow-open","dialog:allow-ask","dialog:allow-message","process:allow-restart","fs:allow-read-file","theme:allow-set-theme","theme:allow-get-theme","menu:allow-new","menu:allow-popup","http:default","shell:allow-open",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["linux","macOS","windows"]}}
|
||||||
@@ -1605,7 +1605,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-app-recursive -> This scope recursive access to the complete `$APP` folder, including sub directories and files.",
|
"description": "fs:scope-app-recursive -> This scope permits recursive access to the complete `$APP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-app-recursive"
|
"fs:scope-app-recursive"
|
||||||
@@ -1626,7 +1626,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appcache-recursive -> This scope recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
"description": "fs:scope-appcache-recursive -> This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appcache-recursive"
|
"fs:scope-appcache-recursive"
|
||||||
@@ -1647,7 +1647,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appconfig-recursive -> This scope recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-appconfig-recursive -> This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appconfig-recursive"
|
"fs:scope-appconfig-recursive"
|
||||||
@@ -1668,7 +1668,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appdata-recursive -> This scope recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
"description": "fs:scope-appdata-recursive -> This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appdata-recursive"
|
"fs:scope-appdata-recursive"
|
||||||
@@ -1689,7 +1689,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applocaldata-recursive -> This scope recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-applocaldata-recursive -> This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applocaldata-recursive"
|
"fs:scope-applocaldata-recursive"
|
||||||
@@ -1710,7 +1710,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applog-recursive -> This scope recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
"description": "fs:scope-applog-recursive -> This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applog-recursive"
|
"fs:scope-applog-recursive"
|
||||||
@@ -1731,7 +1731,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-audio-recursive -> This scope recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
"description": "fs:scope-audio-recursive -> This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-audio-recursive"
|
"fs:scope-audio-recursive"
|
||||||
@@ -1752,7 +1752,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-cache-recursive -> This scope recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
"description": "fs:scope-cache-recursive -> This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-cache-recursive"
|
"fs:scope-cache-recursive"
|
||||||
@@ -1773,7 +1773,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-config-recursive -> This scope recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-config-recursive -> This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-config-recursive"
|
"fs:scope-config-recursive"
|
||||||
@@ -1794,7 +1794,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-data-recursive -> This scope recursive access to the complete `$DATA` folder, including sub directories and files.",
|
"description": "fs:scope-data-recursive -> This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-data-recursive"
|
"fs:scope-data-recursive"
|
||||||
@@ -1815,7 +1815,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-desktop-recursive -> This scope recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
"description": "fs:scope-desktop-recursive -> This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-desktop-recursive"
|
"fs:scope-desktop-recursive"
|
||||||
@@ -1836,7 +1836,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-document-recursive -> This scope recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
"description": "fs:scope-document-recursive -> This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-document-recursive"
|
"fs:scope-document-recursive"
|
||||||
@@ -1857,7 +1857,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-download-recursive -> This scope recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
"description": "fs:scope-download-recursive -> This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-download-recursive"
|
"fs:scope-download-recursive"
|
||||||
@@ -1878,7 +1878,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-exe-recursive -> This scope recursive access to the complete `$EXE` folder, including sub directories and files.",
|
"description": "fs:scope-exe-recursive -> This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-exe-recursive"
|
"fs:scope-exe-recursive"
|
||||||
@@ -1899,7 +1899,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-font-recursive -> This scope recursive access to the complete `$FONT` folder, including sub directories and files.",
|
"description": "fs:scope-font-recursive -> This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-font-recursive"
|
"fs:scope-font-recursive"
|
||||||
@@ -1920,7 +1920,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-home-recursive -> This scope recursive access to the complete `$HOME` folder, including sub directories and files.",
|
"description": "fs:scope-home-recursive -> This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-home-recursive"
|
"fs:scope-home-recursive"
|
||||||
@@ -1941,7 +1941,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-localdata-recursive -> This scope recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-localdata-recursive -> This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-localdata-recursive"
|
"fs:scope-localdata-recursive"
|
||||||
@@ -1962,7 +1962,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-log-recursive -> This scope recursive access to the complete `$LOG` folder, including sub directories and files.",
|
"description": "fs:scope-log-recursive -> This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-log-recursive"
|
"fs:scope-log-recursive"
|
||||||
@@ -1983,7 +1983,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-picture-recursive -> This scope recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
"description": "fs:scope-picture-recursive -> This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-picture-recursive"
|
"fs:scope-picture-recursive"
|
||||||
@@ -2004,7 +2004,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-public-recursive -> This scope recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
"description": "fs:scope-public-recursive -> This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-public-recursive"
|
"fs:scope-public-recursive"
|
||||||
@@ -2025,7 +2025,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-resource-recursive -> This scope recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
"description": "fs:scope-resource-recursive -> This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-resource-recursive"
|
"fs:scope-resource-recursive"
|
||||||
@@ -2046,7 +2046,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-runtime-recursive -> This scope recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
"description": "fs:scope-runtime-recursive -> This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-runtime-recursive"
|
"fs:scope-runtime-recursive"
|
||||||
@@ -2067,7 +2067,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-temp-recursive -> This scope recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
"description": "fs:scope-temp-recursive -> This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-temp-recursive"
|
"fs:scope-temp-recursive"
|
||||||
@@ -2088,7 +2088,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-template-recursive -> This scope recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
"description": "fs:scope-template-recursive -> This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-template-recursive"
|
"fs:scope-template-recursive"
|
||||||
@@ -2109,7 +2109,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-video-recursive -> This scope recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
"description": "fs:scope-video-recursive -> This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-video-recursive"
|
"fs:scope-video-recursive"
|
||||||
@@ -4258,7 +4258,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-app-recursive -> This scope recursive access to the complete `$APP` folder, including sub directories and files.",
|
"description": "fs:scope-app-recursive -> This scope permits recursive access to the complete `$APP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-app-recursive"
|
"fs:scope-app-recursive"
|
||||||
@@ -4279,7 +4279,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appcache-recursive -> This scope recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
"description": "fs:scope-appcache-recursive -> This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appcache-recursive"
|
"fs:scope-appcache-recursive"
|
||||||
@@ -4300,7 +4300,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appconfig-recursive -> This scope recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-appconfig-recursive -> This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appconfig-recursive"
|
"fs:scope-appconfig-recursive"
|
||||||
@@ -4321,7 +4321,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appdata-recursive -> This scope recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
"description": "fs:scope-appdata-recursive -> This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appdata-recursive"
|
"fs:scope-appdata-recursive"
|
||||||
@@ -4342,7 +4342,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applocaldata-recursive -> This scope recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-applocaldata-recursive -> This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applocaldata-recursive"
|
"fs:scope-applocaldata-recursive"
|
||||||
@@ -4363,7 +4363,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applog-recursive -> This scope recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
"description": "fs:scope-applog-recursive -> This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applog-recursive"
|
"fs:scope-applog-recursive"
|
||||||
@@ -4384,7 +4384,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-audio-recursive -> This scope recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
"description": "fs:scope-audio-recursive -> This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-audio-recursive"
|
"fs:scope-audio-recursive"
|
||||||
@@ -4405,7 +4405,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-cache-recursive -> This scope recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
"description": "fs:scope-cache-recursive -> This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-cache-recursive"
|
"fs:scope-cache-recursive"
|
||||||
@@ -4426,7 +4426,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-config-recursive -> This scope recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-config-recursive -> This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-config-recursive"
|
"fs:scope-config-recursive"
|
||||||
@@ -4447,7 +4447,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-data-recursive -> This scope recursive access to the complete `$DATA` folder, including sub directories and files.",
|
"description": "fs:scope-data-recursive -> This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-data-recursive"
|
"fs:scope-data-recursive"
|
||||||
@@ -4468,7 +4468,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-desktop-recursive -> This scope recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
"description": "fs:scope-desktop-recursive -> This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-desktop-recursive"
|
"fs:scope-desktop-recursive"
|
||||||
@@ -4489,7 +4489,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-document-recursive -> This scope recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
"description": "fs:scope-document-recursive -> This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-document-recursive"
|
"fs:scope-document-recursive"
|
||||||
@@ -4510,7 +4510,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-download-recursive -> This scope recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
"description": "fs:scope-download-recursive -> This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-download-recursive"
|
"fs:scope-download-recursive"
|
||||||
@@ -4531,7 +4531,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-exe-recursive -> This scope recursive access to the complete `$EXE` folder, including sub directories and files.",
|
"description": "fs:scope-exe-recursive -> This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-exe-recursive"
|
"fs:scope-exe-recursive"
|
||||||
@@ -4552,7 +4552,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-font-recursive -> This scope recursive access to the complete `$FONT` folder, including sub directories and files.",
|
"description": "fs:scope-font-recursive -> This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-font-recursive"
|
"fs:scope-font-recursive"
|
||||||
@@ -4573,7 +4573,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-home-recursive -> This scope recursive access to the complete `$HOME` folder, including sub directories and files.",
|
"description": "fs:scope-home-recursive -> This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-home-recursive"
|
"fs:scope-home-recursive"
|
||||||
@@ -4594,7 +4594,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-localdata-recursive -> This scope recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-localdata-recursive -> This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-localdata-recursive"
|
"fs:scope-localdata-recursive"
|
||||||
@@ -4615,7 +4615,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-log-recursive -> This scope recursive access to the complete `$LOG` folder, including sub directories and files.",
|
"description": "fs:scope-log-recursive -> This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-log-recursive"
|
"fs:scope-log-recursive"
|
||||||
@@ -4636,7 +4636,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-picture-recursive -> This scope recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
"description": "fs:scope-picture-recursive -> This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-picture-recursive"
|
"fs:scope-picture-recursive"
|
||||||
@@ -4657,7 +4657,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-public-recursive -> This scope recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
"description": "fs:scope-public-recursive -> This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-public-recursive"
|
"fs:scope-public-recursive"
|
||||||
@@ -4678,7 +4678,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-resource-recursive -> This scope recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
"description": "fs:scope-resource-recursive -> This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-resource-recursive"
|
"fs:scope-resource-recursive"
|
||||||
@@ -4699,7 +4699,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-runtime-recursive -> This scope recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
"description": "fs:scope-runtime-recursive -> This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-runtime-recursive"
|
"fs:scope-runtime-recursive"
|
||||||
@@ -4720,7 +4720,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-temp-recursive -> This scope recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
"description": "fs:scope-temp-recursive -> This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-temp-recursive"
|
"fs:scope-temp-recursive"
|
||||||
@@ -4741,7 +4741,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-template-recursive -> This scope recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
"description": "fs:scope-template-recursive -> This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-template-recursive"
|
"fs:scope-template-recursive"
|
||||||
@@ -4762,7 +4762,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-video-recursive -> This scope recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
"description": "fs:scope-video-recursive -> This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-video-recursive"
|
"fs:scope-video-recursive"
|
||||||
@@ -5258,6 +5258,20 @@
|
|||||||
"notification:allow-notify"
|
"notification:allow-notify"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:allow-register-action-types -> Enables the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:allow-register-action-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:allow-register-listener -> Enables the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:allow-register-listener"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "notification:allow-request-permission -> Enables the request_permission command without any pre-configured scope.",
|
"description": "notification:allow-request-permission -> Enables the request_permission command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5279,6 +5293,20 @@
|
|||||||
"notification:deny-notify"
|
"notification:deny-notify"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:deny-register-action-types -> Denies the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:deny-register-action-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:deny-register-listener -> Denies the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:deny-register-listener"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "notification:deny-request-permission -> Denies the request_permission command without any pre-configured scope.",
|
"description": "notification:deny-request-permission -> Denies the request_permission command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -1605,7 +1605,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-app-recursive -> This scope recursive access to the complete `$APP` folder, including sub directories and files.",
|
"description": "fs:scope-app-recursive -> This scope permits recursive access to the complete `$APP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-app-recursive"
|
"fs:scope-app-recursive"
|
||||||
@@ -1626,7 +1626,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appcache-recursive -> This scope recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
"description": "fs:scope-appcache-recursive -> This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appcache-recursive"
|
"fs:scope-appcache-recursive"
|
||||||
@@ -1647,7 +1647,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appconfig-recursive -> This scope recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-appconfig-recursive -> This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appconfig-recursive"
|
"fs:scope-appconfig-recursive"
|
||||||
@@ -1668,7 +1668,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appdata-recursive -> This scope recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
"description": "fs:scope-appdata-recursive -> This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appdata-recursive"
|
"fs:scope-appdata-recursive"
|
||||||
@@ -1689,7 +1689,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applocaldata-recursive -> This scope recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-applocaldata-recursive -> This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applocaldata-recursive"
|
"fs:scope-applocaldata-recursive"
|
||||||
@@ -1710,7 +1710,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applog-recursive -> This scope recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
"description": "fs:scope-applog-recursive -> This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applog-recursive"
|
"fs:scope-applog-recursive"
|
||||||
@@ -1731,7 +1731,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-audio-recursive -> This scope recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
"description": "fs:scope-audio-recursive -> This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-audio-recursive"
|
"fs:scope-audio-recursive"
|
||||||
@@ -1752,7 +1752,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-cache-recursive -> This scope recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
"description": "fs:scope-cache-recursive -> This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-cache-recursive"
|
"fs:scope-cache-recursive"
|
||||||
@@ -1773,7 +1773,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-config-recursive -> This scope recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-config-recursive -> This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-config-recursive"
|
"fs:scope-config-recursive"
|
||||||
@@ -1794,7 +1794,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-data-recursive -> This scope recursive access to the complete `$DATA` folder, including sub directories and files.",
|
"description": "fs:scope-data-recursive -> This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-data-recursive"
|
"fs:scope-data-recursive"
|
||||||
@@ -1815,7 +1815,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-desktop-recursive -> This scope recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
"description": "fs:scope-desktop-recursive -> This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-desktop-recursive"
|
"fs:scope-desktop-recursive"
|
||||||
@@ -1836,7 +1836,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-document-recursive -> This scope recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
"description": "fs:scope-document-recursive -> This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-document-recursive"
|
"fs:scope-document-recursive"
|
||||||
@@ -1857,7 +1857,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-download-recursive -> This scope recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
"description": "fs:scope-download-recursive -> This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-download-recursive"
|
"fs:scope-download-recursive"
|
||||||
@@ -1878,7 +1878,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-exe-recursive -> This scope recursive access to the complete `$EXE` folder, including sub directories and files.",
|
"description": "fs:scope-exe-recursive -> This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-exe-recursive"
|
"fs:scope-exe-recursive"
|
||||||
@@ -1899,7 +1899,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-font-recursive -> This scope recursive access to the complete `$FONT` folder, including sub directories and files.",
|
"description": "fs:scope-font-recursive -> This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-font-recursive"
|
"fs:scope-font-recursive"
|
||||||
@@ -1920,7 +1920,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-home-recursive -> This scope recursive access to the complete `$HOME` folder, including sub directories and files.",
|
"description": "fs:scope-home-recursive -> This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-home-recursive"
|
"fs:scope-home-recursive"
|
||||||
@@ -1941,7 +1941,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-localdata-recursive -> This scope recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-localdata-recursive -> This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-localdata-recursive"
|
"fs:scope-localdata-recursive"
|
||||||
@@ -1962,7 +1962,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-log-recursive -> This scope recursive access to the complete `$LOG` folder, including sub directories and files.",
|
"description": "fs:scope-log-recursive -> This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-log-recursive"
|
"fs:scope-log-recursive"
|
||||||
@@ -1983,7 +1983,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-picture-recursive -> This scope recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
"description": "fs:scope-picture-recursive -> This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-picture-recursive"
|
"fs:scope-picture-recursive"
|
||||||
@@ -2004,7 +2004,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-public-recursive -> This scope recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
"description": "fs:scope-public-recursive -> This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-public-recursive"
|
"fs:scope-public-recursive"
|
||||||
@@ -2025,7 +2025,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-resource-recursive -> This scope recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
"description": "fs:scope-resource-recursive -> This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-resource-recursive"
|
"fs:scope-resource-recursive"
|
||||||
@@ -2046,7 +2046,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-runtime-recursive -> This scope recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
"description": "fs:scope-runtime-recursive -> This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-runtime-recursive"
|
"fs:scope-runtime-recursive"
|
||||||
@@ -2067,7 +2067,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-temp-recursive -> This scope recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
"description": "fs:scope-temp-recursive -> This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-temp-recursive"
|
"fs:scope-temp-recursive"
|
||||||
@@ -2088,7 +2088,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-template-recursive -> This scope recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
"description": "fs:scope-template-recursive -> This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-template-recursive"
|
"fs:scope-template-recursive"
|
||||||
@@ -2109,7 +2109,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-video-recursive -> This scope recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
"description": "fs:scope-video-recursive -> This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-video-recursive"
|
"fs:scope-video-recursive"
|
||||||
@@ -4258,7 +4258,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-app-recursive -> This scope recursive access to the complete `$APP` folder, including sub directories and files.",
|
"description": "fs:scope-app-recursive -> This scope permits recursive access to the complete `$APP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-app-recursive"
|
"fs:scope-app-recursive"
|
||||||
@@ -4279,7 +4279,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appcache-recursive -> This scope recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
"description": "fs:scope-appcache-recursive -> This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appcache-recursive"
|
"fs:scope-appcache-recursive"
|
||||||
@@ -4300,7 +4300,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appconfig-recursive -> This scope recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-appconfig-recursive -> This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appconfig-recursive"
|
"fs:scope-appconfig-recursive"
|
||||||
@@ -4321,7 +4321,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-appdata-recursive -> This scope recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
"description": "fs:scope-appdata-recursive -> This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-appdata-recursive"
|
"fs:scope-appdata-recursive"
|
||||||
@@ -4342,7 +4342,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applocaldata-recursive -> This scope recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-applocaldata-recursive -> This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applocaldata-recursive"
|
"fs:scope-applocaldata-recursive"
|
||||||
@@ -4363,7 +4363,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-applog-recursive -> This scope recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
"description": "fs:scope-applog-recursive -> This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-applog-recursive"
|
"fs:scope-applog-recursive"
|
||||||
@@ -4384,7 +4384,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-audio-recursive -> This scope recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
"description": "fs:scope-audio-recursive -> This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-audio-recursive"
|
"fs:scope-audio-recursive"
|
||||||
@@ -4405,7 +4405,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-cache-recursive -> This scope recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
"description": "fs:scope-cache-recursive -> This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-cache-recursive"
|
"fs:scope-cache-recursive"
|
||||||
@@ -4426,7 +4426,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-config-recursive -> This scope recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
"description": "fs:scope-config-recursive -> This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-config-recursive"
|
"fs:scope-config-recursive"
|
||||||
@@ -4447,7 +4447,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-data-recursive -> This scope recursive access to the complete `$DATA` folder, including sub directories and files.",
|
"description": "fs:scope-data-recursive -> This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-data-recursive"
|
"fs:scope-data-recursive"
|
||||||
@@ -4468,7 +4468,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-desktop-recursive -> This scope recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
"description": "fs:scope-desktop-recursive -> This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-desktop-recursive"
|
"fs:scope-desktop-recursive"
|
||||||
@@ -4489,7 +4489,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-document-recursive -> This scope recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
"description": "fs:scope-document-recursive -> This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-document-recursive"
|
"fs:scope-document-recursive"
|
||||||
@@ -4510,7 +4510,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-download-recursive -> This scope recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
"description": "fs:scope-download-recursive -> This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-download-recursive"
|
"fs:scope-download-recursive"
|
||||||
@@ -4531,7 +4531,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-exe-recursive -> This scope recursive access to the complete `$EXE` folder, including sub directories and files.",
|
"description": "fs:scope-exe-recursive -> This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-exe-recursive"
|
"fs:scope-exe-recursive"
|
||||||
@@ -4552,7 +4552,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-font-recursive -> This scope recursive access to the complete `$FONT` folder, including sub directories and files.",
|
"description": "fs:scope-font-recursive -> This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-font-recursive"
|
"fs:scope-font-recursive"
|
||||||
@@ -4573,7 +4573,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-home-recursive -> This scope recursive access to the complete `$HOME` folder, including sub directories and files.",
|
"description": "fs:scope-home-recursive -> This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-home-recursive"
|
"fs:scope-home-recursive"
|
||||||
@@ -4594,7 +4594,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-localdata-recursive -> This scope recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
"description": "fs:scope-localdata-recursive -> This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-localdata-recursive"
|
"fs:scope-localdata-recursive"
|
||||||
@@ -4615,7 +4615,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-log-recursive -> This scope recursive access to the complete `$LOG` folder, including sub directories and files.",
|
"description": "fs:scope-log-recursive -> This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-log-recursive"
|
"fs:scope-log-recursive"
|
||||||
@@ -4636,7 +4636,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-picture-recursive -> This scope recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
"description": "fs:scope-picture-recursive -> This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-picture-recursive"
|
"fs:scope-picture-recursive"
|
||||||
@@ -4657,7 +4657,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-public-recursive -> This scope recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
"description": "fs:scope-public-recursive -> This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-public-recursive"
|
"fs:scope-public-recursive"
|
||||||
@@ -4678,7 +4678,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-resource-recursive -> This scope recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
"description": "fs:scope-resource-recursive -> This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-resource-recursive"
|
"fs:scope-resource-recursive"
|
||||||
@@ -4699,7 +4699,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-runtime-recursive -> This scope recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
"description": "fs:scope-runtime-recursive -> This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-runtime-recursive"
|
"fs:scope-runtime-recursive"
|
||||||
@@ -4720,7 +4720,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-temp-recursive -> This scope recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
"description": "fs:scope-temp-recursive -> This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-temp-recursive"
|
"fs:scope-temp-recursive"
|
||||||
@@ -4741,7 +4741,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-template-recursive -> This scope recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
"description": "fs:scope-template-recursive -> This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-template-recursive"
|
"fs:scope-template-recursive"
|
||||||
@@ -4762,7 +4762,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "fs:scope-video-recursive -> This scope recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
"description": "fs:scope-video-recursive -> This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"fs:scope-video-recursive"
|
"fs:scope-video-recursive"
|
||||||
@@ -5258,6 +5258,20 @@
|
|||||||
"notification:allow-notify"
|
"notification:allow-notify"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:allow-register-action-types -> Enables the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:allow-register-action-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:allow-register-listener -> Enables the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:allow-register-listener"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "notification:allow-request-permission -> Enables the request_permission command without any pre-configured scope.",
|
"description": "notification:allow-request-permission -> Enables the request_permission command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -5279,6 +5293,20 @@
|
|||||||
"notification:deny-notify"
|
"notification:deny-notify"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:deny-register-action-types -> Denies the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:deny-register-action-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "notification:deny-register-listener -> Denies the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"notification:deny-register-listener"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "notification:deny-request-permission -> Denies the request_permission command without any pre-configured scope.",
|
"description": "notification:deny-request-permission -> Denies the request_permission command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ use std::str::FromStr;
|
|||||||
use cocoa::{appkit::NSApp, base::nil, foundation::NSString};
|
use cocoa::{appkit::NSApp, base::nil, foundation::NSString};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specta::Type;
|
use specta::Type;
|
||||||
use tauri::{LogicalPosition, LogicalSize, Manager, State, WebviewUrl};
|
use tauri::utils::config::WindowEffectsConfig;
|
||||||
|
use tauri::window::Effect;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use tauri::TitleBarStyle;
|
use tauri::TitleBarStyle;
|
||||||
use tauri::utils::config::WindowEffectsConfig;
|
|
||||||
use tauri::WebviewWindowBuilder;
|
use tauri::WebviewWindowBuilder;
|
||||||
use tauri::window::Effect;
|
use tauri::{LogicalPosition, LogicalSize, Manager, State, WebviewUrl};
|
||||||
use tauri_plugin_decorum::WebviewWindowExt;
|
use tauri_plugin_decorum::WebviewWindowExt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
@@ -25,39 +25,45 @@ pub struct Window {
|
|||||||
height: f64,
|
height: f64,
|
||||||
maximizable: bool,
|
maximizable: bool,
|
||||||
minimizable: bool,
|
minimizable: bool,
|
||||||
|
hidden_title: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Type)]
|
||||||
|
pub struct Column {
|
||||||
|
label: String,
|
||||||
|
url: String,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub fn create_column(
|
pub fn create_column(
|
||||||
label: &str,
|
column: Column,
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
url: &str,
|
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: State<'_, Nostr>,
|
state: State<'_, Nostr>,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
let settings = state.settings.lock().unwrap().clone();
|
let settings = state.settings.lock().unwrap().clone();
|
||||||
|
|
||||||
match app_handle.get_window("main") {
|
match app_handle.get_window("main") {
|
||||||
Some(main_window) => match app_handle.get_webview(label) {
|
Some(main_window) => match app_handle.get_webview(&column.label) {
|
||||||
Some(_) => Ok(label.into()),
|
Some(_) => Ok(column.label),
|
||||||
None => {
|
None => {
|
||||||
let path = PathBuf::from(url);
|
let path = PathBuf::from(column.url);
|
||||||
let webview_url = WebviewUrl::App(path);
|
let webview_url = WebviewUrl::App(path);
|
||||||
let builder = match settings.proxy {
|
let builder = match settings.proxy {
|
||||||
Some(url) => {
|
Some(url) => {
|
||||||
let proxy = Url::from_str(&url).unwrap();
|
let proxy = Url::from_str(&url).unwrap();
|
||||||
tauri::webview::WebviewBuilder::new(label, webview_url)
|
tauri::webview::WebviewBuilder::new(column.label, webview_url)
|
||||||
.user_agent("Lume/4.0")
|
.user_agent("Lume/4.0")
|
||||||
.zoom_hotkeys_enabled(true)
|
.zoom_hotkeys_enabled(true)
|
||||||
.enable_clipboard_access()
|
.enable_clipboard_access()
|
||||||
.transparent(true)
|
.transparent(true)
|
||||||
.proxy_url(proxy)
|
.proxy_url(proxy)
|
||||||
}
|
}
|
||||||
None => tauri::webview::WebviewBuilder::new(label, webview_url)
|
None => tauri::webview::WebviewBuilder::new(column.label, webview_url)
|
||||||
.user_agent("Lume/4.0")
|
.user_agent("Lume/4.0")
|
||||||
.zoom_hotkeys_enabled(true)
|
.zoom_hotkeys_enabled(true)
|
||||||
.enable_clipboard_access()
|
.enable_clipboard_access()
|
||||||
@@ -65,8 +71,8 @@ pub fn create_column(
|
|||||||
};
|
};
|
||||||
match main_window.add_child(
|
match main_window.add_child(
|
||||||
builder,
|
builder,
|
||||||
LogicalPosition::new(x, y),
|
LogicalPosition::new(column.x, column.y),
|
||||||
LogicalSize::new(width, height),
|
LogicalSize::new(column.width, column.height),
|
||||||
) {
|
) {
|
||||||
Ok(webview) => Ok(webview.label().into()),
|
Ok(webview) => Ok(webview.label().into()),
|
||||||
Err(_) => Err("Create webview failed".into()),
|
Err(_) => Err("Create webview failed".into()),
|
||||||
@@ -79,7 +85,7 @@ pub fn create_column(
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub fn close_column(label: &str, app_handle: tauri::AppHandle) -> Result<bool, ()> {
|
pub fn close_column(label: &str, app_handle: tauri::AppHandle) -> Result<bool, String> {
|
||||||
match app_handle.get_webview(label) {
|
match app_handle.get_webview(label) {
|
||||||
Some(webview) => {
|
Some(webview) => {
|
||||||
if webview.close().is_ok() {
|
if webview.close().is_ok() {
|
||||||
@@ -88,7 +94,7 @@ pub fn close_column(label: &str, app_handle: tauri::AppHandle) -> Result<bool, (
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Ok(true),
|
None => Err("Column not found.".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,14 +158,13 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
|
|||||||
.title(&window.title)
|
.title(&window.title)
|
||||||
.min_inner_size(window.width, window.height)
|
.min_inner_size(window.width, window.height)
|
||||||
.inner_size(window.width, window.height)
|
.inner_size(window.width, window.height)
|
||||||
.hidden_title(true)
|
.hidden_title(window.hidden_title)
|
||||||
.title_bar_style(TitleBarStyle::Overlay)
|
.title_bar_style(TitleBarStyle::Overlay)
|
||||||
.transparent(true)
|
|
||||||
.minimizable(window.minimizable)
|
.minimizable(window.minimizable)
|
||||||
.maximizable(window.maximizable)
|
.maximizable(window.maximizable)
|
||||||
.effects(WindowEffectsConfig {
|
.effects(WindowEffectsConfig {
|
||||||
state: None,
|
state: None,
|
||||||
effects: vec![Effect::WindowBackground],
|
effects: vec![Effect::UnderWindowBackground],
|
||||||
radius: None,
|
radius: None,
|
||||||
color: None,
|
color: None,
|
||||||
})
|
})
|
||||||
@@ -171,7 +176,6 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
|
|||||||
.title(title)
|
.title(title)
|
||||||
.min_inner_size(width, height)
|
.min_inner_size(width, height)
|
||||||
.inner_size(width, height)
|
.inner_size(width, height)
|
||||||
.transparent(true)
|
|
||||||
.effects(WindowEffectsConfig {
|
.effects(WindowEffectsConfig {
|
||||||
state: None,
|
state: None,
|
||||||
effects: vec![Effect::Mica],
|
effects: vec![Effect::Mica],
|
||||||
@@ -189,9 +193,12 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
|
|||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
// Set decoration
|
||||||
// Create a custom titlebar for Windows
|
|
||||||
window.create_overlay_titlebar().unwrap();
|
window.create_overlay_titlebar().unwrap();
|
||||||
|
|
||||||
|
// Make main window transparent
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
window.make_transparent().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -223,9 +230,13 @@ pub fn open_main_window(app: tauri::AppHandle) {
|
|||||||
let _ = window.set_focus();
|
let _ = window.set_focus();
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let _ = WebviewWindowBuilder::from_config(&app, app.config().app.windows.first().unwrap())
|
let window = WebviewWindowBuilder::from_config(&app, app.config().app.windows.first().unwrap())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Make main window transparent
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
window.make_transparent().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use tauri_nspanel::{
|
|||||||
objc::{class, msg_send, runtime::NO, sel, sel_impl},
|
objc::{class, msg_send, runtime::NO, sel, sel_impl},
|
||||||
panel_delegate, ManagerExt, WebviewWindowExt,
|
panel_delegate, ManagerExt, WebviewWindowExt,
|
||||||
};
|
};
|
||||||
|
use tauri_plugin_decorum::WebviewWindowExt as WebviewWindowExt2;
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
const NSWindowStyleMaskNonActivatingPanel: i32 = 1 << 7;
|
const NSWindowStyleMaskNonActivatingPanel: i32 = 1 << 7;
|
||||||
@@ -22,9 +23,9 @@ pub fn swizzle_to_menubar_panel(app_handle: &tauri::AppHandle) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let window = app_handle.get_webview_window("panel").unwrap();
|
let window = app_handle.get_webview_window("panel").unwrap();
|
||||||
|
window.make_transparent().unwrap();
|
||||||
|
|
||||||
let panel = window.to_panel().unwrap();
|
let panel = window.to_panel().unwrap();
|
||||||
|
|
||||||
let handle = app_handle.clone();
|
let handle = app_handle.clone();
|
||||||
|
|
||||||
panel_delegate.set_listener(Box::new(move |delegate_name: String| {
|
panel_delegate.set_listener(Box::new(move |delegate_name: String| {
|
||||||
|
|||||||
@@ -9,20 +9,20 @@ extern crate cocoa;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate objc;
|
extern crate objc;
|
||||||
|
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
io::{self, BufRead},
|
io::{self, BufRead},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specta::Type;
|
use specta::Type;
|
||||||
use tauri::{Manager, path::BaseDirectory};
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use tauri::tray::{MouseButtonState, TrayIconEvent};
|
use tauri::tray::{MouseButtonState, TrayIconEvent};
|
||||||
|
use tauri::{path::BaseDirectory, Manager};
|
||||||
use tauri_nspanel::ManagerExt;
|
use tauri_nspanel::ManagerExt;
|
||||||
use tauri_plugin_decorum::WebviewWindowExt;
|
use tauri_plugin_decorum::WebviewWindowExt;
|
||||||
|
|
||||||
@@ -112,6 +112,8 @@ fn main() {
|
|||||||
nostr::event::get_event,
|
nostr::event::get_event,
|
||||||
nostr::event::get_event_from,
|
nostr::event::get_event_from,
|
||||||
nostr::event::get_replies,
|
nostr::event::get_replies,
|
||||||
|
nostr::event::listen_event_reply,
|
||||||
|
nostr::event::unlisten_event_reply,
|
||||||
nostr::event::get_events_by,
|
nostr::event::get_events_by,
|
||||||
nostr::event::get_local_events,
|
nostr::event::get_local_events,
|
||||||
nostr::event::get_group_events,
|
nostr::event::get_group_events,
|
||||||
@@ -142,10 +144,14 @@ fn main() {
|
|||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let main_window = app.get_webview_window("main").unwrap();
|
let main_window = app.get_webview_window("main").unwrap();
|
||||||
|
|
||||||
// Create a custom titlebar for Windows
|
// Set custom decoration for Windows
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
main_window.create_overlay_titlebar().unwrap();
|
main_window.create_overlay_titlebar().unwrap();
|
||||||
|
|
||||||
|
// Make main window transparent
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
main_window.make_transparent().unwrap();
|
||||||
|
|
||||||
// Set a custom inset to the traffic lights
|
// Set a custom inset to the traffic lights
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
main_window.set_traffic_lights_inset(8.0, 16.0).unwrap();
|
main_window.set_traffic_lights_inset(8.0, 16.0).unwrap();
|
||||||
@@ -192,7 +198,8 @@ fn main() {
|
|||||||
// Config
|
// Config
|
||||||
let opts = Options::new()
|
let opts = Options::new()
|
||||||
.automatic_authentication(true)
|
.automatic_authentication(true)
|
||||||
.connection_timeout(Some(Duration::from_secs(5)));
|
.connection_timeout(Some(Duration::from_secs(5)))
|
||||||
|
.timeout(Duration::from_secs(30));
|
||||||
|
|
||||||
// Setup nostr client
|
// Setup nostr client
|
||||||
let client = match database {
|
let client = match database {
|
||||||
@@ -201,29 +208,30 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get bootstrap relays
|
// Get bootstrap relays
|
||||||
let relays_path = app
|
if let Ok(path) = app
|
||||||
.path()
|
.path()
|
||||||
.resolve("resources/relays.txt", BaseDirectory::Resource)
|
.resolve("resources/relays.txt", BaseDirectory::Resource)
|
||||||
.expect("Bootstrap relays not found.");
|
{
|
||||||
let file = std::fs::File::open(&relays_path).unwrap();
|
let file = std::fs::File::open(&path).unwrap();
|
||||||
let lines = io::BufReader::new(file).lines();
|
let lines = io::BufReader::new(file).lines();
|
||||||
|
|
||||||
// Add bootstrap relays to relay pool
|
// Add bootstrap relays to relay pool
|
||||||
for line in lines.map_while(Result::ok) {
|
for line in lines.map_while(Result::ok) {
|
||||||
if let Some((relay, option)) = line.split_once(',') {
|
if let Some((relay, option)) = line.split_once(',') {
|
||||||
match RelayMetadata::from_str(option) {
|
match RelayMetadata::from_str(option) {
|
||||||
Ok(meta) => {
|
Ok(meta) => {
|
||||||
println!("connecting to bootstrap relay...: {} - {}", relay, meta);
|
println!("connecting to bootstrap relay...: {} - {}", relay, meta);
|
||||||
let opts = if meta == RelayMetadata::Read {
|
let opts = if meta == RelayMetadata::Read {
|
||||||
RelayOptions::new().read(true).write(false)
|
RelayOptions::new().read(true).write(false)
|
||||||
} else {
|
} else {
|
||||||
RelayOptions::new().write(true).read(false)
|
RelayOptions::new().write(true).read(false)
|
||||||
};
|
};
|
||||||
let _ = client.add_relay_with_opts(relay, opts).await;
|
let _ = client.add_relay_with_opts(relay, opts).await;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("connecting to bootstrap relay...: {}", relay);
|
println!("connecting to bootstrap relay...: {}", relay);
|
||||||
let _ = client.add_relay(relay).await;
|
let _ = client.add_relay(relay).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use tauri::State;
|
|||||||
use crate::nostr::utils::{create_event_tags, dedup_event, parse_event, Meta};
|
use crate::nostr::utils::{create_event_tags, dedup_event, parse_event, Meta};
|
||||||
use crate::Nostr;
|
use crate::Nostr;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Type)]
|
#[derive(Debug, Clone, Serialize, Type)]
|
||||||
pub struct RichEvent {
|
pub struct RichEvent {
|
||||||
pub raw: String,
|
pub raw: String,
|
||||||
pub parsed: Option<Meta>,
|
pub parsed: Option<Meta>,
|
||||||
@@ -146,33 +146,69 @@ pub async fn get_event_from(
|
|||||||
pub async fn get_replies(id: &str, state: State<'_, Nostr>) -> Result<Vec<RichEvent>, String> {
|
pub async fn get_replies(id: &str, state: State<'_, Nostr>) -> Result<Vec<RichEvent>, String> {
|
||||||
let client = &state.client;
|
let client = &state.client;
|
||||||
|
|
||||||
match EventId::from_hex(id) {
|
let event_id = match EventId::from_hex(id) {
|
||||||
Ok(event_id) => {
|
Ok(id) => id,
|
||||||
let filter = Filter::new().kinds(vec![Kind::TextNote]).event(event_id);
|
Err(err) => return Err(err.to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
match client.get_events_of(vec![filter], None).await {
|
let filter = Filter::new().kinds(vec![Kind::TextNote]).event(event_id);
|
||||||
Ok(events) => {
|
|
||||||
let futures = events.into_iter().map(|ev| async move {
|
|
||||||
let raw = ev.as_json();
|
|
||||||
let parsed = if ev.kind == Kind::TextNote {
|
|
||||||
Some(parse_event(&ev.content).await)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
RichEvent { raw, parsed }
|
match client.get_events_of(vec![filter], None).await {
|
||||||
});
|
Ok(events) => {
|
||||||
let rich_events = join_all(futures).await;
|
let futures = events.into_iter().map(|ev| async move {
|
||||||
|
let raw = ev.as_json();
|
||||||
|
let parsed = if ev.kind == Kind::TextNote {
|
||||||
|
Some(parse_event(&ev.content).await)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
Ok(rich_events)
|
RichEvent { raw, parsed }
|
||||||
}
|
});
|
||||||
Err(err) => Err(err.to_string()),
|
let rich_events = join_all(futures).await;
|
||||||
}
|
|
||||||
|
Ok(rich_events)
|
||||||
}
|
}
|
||||||
Err(_) => Err("Event ID is not valid".into()),
|
Err(err) => Err(err.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub async fn listen_event_reply(id: &str, state: State<'_, Nostr>) -> Result<(), String> {
|
||||||
|
let client = &state.client;
|
||||||
|
|
||||||
|
let mut label = "event-".to_owned();
|
||||||
|
label.push_str(id);
|
||||||
|
|
||||||
|
let sub_id = SubscriptionId::new(label);
|
||||||
|
let event_id = match EventId::from_hex(id) {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(err) => return Err(err.to_string()),
|
||||||
|
};
|
||||||
|
let filter = Filter::new()
|
||||||
|
.kinds(vec![Kind::TextNote])
|
||||||
|
.event(event_id)
|
||||||
|
.since(Timestamp::now());
|
||||||
|
|
||||||
|
// Subscribe
|
||||||
|
client.subscribe_with_id(sub_id, vec![filter], None).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub async fn unlisten_event_reply(id: &str, state: State<'_, Nostr>) -> Result<(), ()> {
|
||||||
|
let client = &state.client;
|
||||||
|
let sub_id = SubscriptionId::new(id);
|
||||||
|
|
||||||
|
// Remove subscription
|
||||||
|
client.unsubscribe(sub_id).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub async fn get_events_by(
|
pub async fn get_events_by(
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use specta::Type;
|
|||||||
use tauri::{EventTarget, Manager, State};
|
use tauri::{EventTarget, Manager, State};
|
||||||
use tauri_plugin_notification::NotificationExt;
|
use tauri_plugin_notification::NotificationExt;
|
||||||
|
|
||||||
|
use crate::nostr::event::RichEvent;
|
||||||
|
use crate::nostr::utils::parse_event;
|
||||||
use crate::{Nostr, Settings};
|
use crate::{Nostr, Settings};
|
||||||
|
|
||||||
#[derive(Serialize, Type)]
|
#[derive(Serialize, Type)]
|
||||||
@@ -44,7 +46,7 @@ pub fn get_accounts() -> Result<Vec<String>, String> {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub fn create_account() -> Result<Account, ()> {
|
pub fn create_account() -> Result<Account, String> {
|
||||||
let keys = Keys::generate();
|
let keys = Keys::generate();
|
||||||
let public_key = keys.public_key();
|
let public_key = keys.public_key();
|
||||||
let secret_key = keys.secret_key().unwrap();
|
let secret_key = keys.secret_key().unwrap();
|
||||||
@@ -57,6 +59,19 @@ pub fn create_account() -> Result<Account, ()> {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub fn get_private_key(npub: &str) -> Result<String, String> {
|
||||||
|
let keyring = Entry::new(npub, "nostr_secret").unwrap();
|
||||||
|
|
||||||
|
if let Ok(nsec) = keyring.get_password() {
|
||||||
|
let secret_key = SecretKey::from_bech32(nsec).unwrap();
|
||||||
|
Ok(secret_key.to_bech32().unwrap())
|
||||||
|
} else {
|
||||||
|
Err("Key not found".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub async fn save_account(
|
pub async fn save_account(
|
||||||
@@ -94,6 +109,56 @@ pub async fn save_account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub async fn connect_remote_account(uri: &str, state: State<'_, Nostr>) -> Result<String, String> {
|
||||||
|
let client = &state.client;
|
||||||
|
|
||||||
|
match NostrConnectURI::parse(uri) {
|
||||||
|
Ok(bunker_uri) => {
|
||||||
|
let app_keys = Keys::generate();
|
||||||
|
let app_secret = app_keys.secret_key().unwrap().to_string();
|
||||||
|
|
||||||
|
// Get remote user
|
||||||
|
let remote_user = bunker_uri.signer_public_key().unwrap();
|
||||||
|
let remote_npub = remote_user.to_bech32().unwrap();
|
||||||
|
|
||||||
|
match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(120), None).await {
|
||||||
|
Ok(signer) => {
|
||||||
|
let keyring = Entry::new(&remote_npub, "nostr_secret").unwrap();
|
||||||
|
let _ = keyring.set_password(&app_secret);
|
||||||
|
|
||||||
|
// Update signer
|
||||||
|
let _ = client.set_signer(Some(signer.into())).await;
|
||||||
|
|
||||||
|
Ok(remote_npub)
|
||||||
|
}
|
||||||
|
Err(err) => Err(err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => Err(err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
pub async fn get_encrypted_key(npub: &str, password: &str) -> Result<String, String> {
|
||||||
|
let keyring = Entry::new(npub, "nostr_secret").unwrap();
|
||||||
|
|
||||||
|
if let Ok(nsec) = keyring.get_password() {
|
||||||
|
let secret_key = SecretKey::from_bech32(nsec).unwrap();
|
||||||
|
let new_key = EncryptedSecretKey::new(&secret_key, password, 16, KeySecurity::Medium);
|
||||||
|
|
||||||
|
if let Ok(key) = new_key {
|
||||||
|
Ok(key.to_bech32().unwrap())
|
||||||
|
} else {
|
||||||
|
Err("Encrypt key failed".into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("Key not found".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub async fn load_account(
|
pub async fn load_account(
|
||||||
@@ -105,175 +170,171 @@ pub async fn load_account(
|
|||||||
let client = &state.client;
|
let client = &state.client;
|
||||||
let keyring = Entry::new(npub, "nostr_secret").unwrap();
|
let keyring = Entry::new(npub, "nostr_secret").unwrap();
|
||||||
|
|
||||||
if let Ok(password) = keyring.get_password() {
|
let password = match keyring.get_password() {
|
||||||
match bunker {
|
Ok(pw) => pw,
|
||||||
Some(uri) => {
|
Err(_) => return Err("Cancelled".into()),
|
||||||
let app_keys = Keys::parse(password).expect("Secret Key is modified, please check again.");
|
};
|
||||||
|
|
||||||
match NostrConnectURI::parse(uri) {
|
match bunker {
|
||||||
Ok(bunker_uri) => {
|
Some(uri) => {
|
||||||
match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(30), None).await {
|
let app_keys = Keys::parse(password).expect("Secret Key is modified, please check again.");
|
||||||
Ok(signer) => client.set_signer(Some(signer.into())).await,
|
|
||||||
Err(err) => return Err(err.to_string()),
|
match NostrConnectURI::parse(uri) {
|
||||||
}
|
Ok(bunker_uri) => {
|
||||||
|
match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(30), None).await {
|
||||||
|
Ok(signer) => client.set_signer(Some(signer.into())).await,
|
||||||
|
Err(err) => return Err(err.to_string()),
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.to_string()),
|
|
||||||
}
|
}
|
||||||
}
|
Err(err) => return Err(err.to_string()),
|
||||||
None => {
|
|
||||||
let keys = Keys::parse(password).expect("Secret Key is modified, please check again.");
|
|
||||||
let signer = NostrSigner::Keys(keys);
|
|
||||||
|
|
||||||
// Update signer
|
|
||||||
client.set_signer(Some(signer)).await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
let keys = Keys::parse(password).expect("Secret Key is modified, please check again.");
|
||||||
|
let signer = NostrSigner::Keys(keys);
|
||||||
|
|
||||||
// Verify signer
|
// Update signer
|
||||||
let signer = client.signer().await.unwrap();
|
client.set_signer(Some(signer)).await;
|
||||||
let public_key = signer.public_key().await.unwrap();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signer
|
||||||
|
let signer = client.signer().await.unwrap();
|
||||||
|
let public_key = signer.public_key().await.unwrap();
|
||||||
|
|
||||||
|
// Connect to user's relay (NIP-65)
|
||||||
|
if let Ok(events) = client
|
||||||
|
.get_events_of(
|
||||||
|
vec![Filter::new()
|
||||||
|
.author(public_key)
|
||||||
|
.kind(Kind::RelayList)
|
||||||
|
.limit(1)],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
if let Some(event) = events.first() {
|
||||||
|
let relay_list = nip65::extract_relay_list(event);
|
||||||
|
for item in relay_list.into_iter() {
|
||||||
|
let relay_url = item.0.to_string();
|
||||||
|
let opts = match item.1 {
|
||||||
|
Some(val) => {
|
||||||
|
if val == &RelayMetadata::Read {
|
||||||
|
RelayOptions::new().read(true).write(false)
|
||||||
|
} else {
|
||||||
|
RelayOptions::new().write(true).read(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => RelayOptions::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add relay to relay pool
|
||||||
|
let _ = client
|
||||||
|
.add_relay_with_opts(&relay_url, opts)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// Connect relay
|
||||||
|
client.connect_relay(relay_url).await.unwrap_or_default();
|
||||||
|
println!("connecting to relay: {} - {:?}", item.0, item.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get user's contact list
|
||||||
|
let contacts = client.get_contact_list(None).await.unwrap();
|
||||||
|
*state.contact_list.lock().unwrap() = contacts;
|
||||||
|
|
||||||
|
// Create a subscription for notification
|
||||||
|
let sub_id = SubscriptionId::new("notification");
|
||||||
|
let filter = Filter::new()
|
||||||
|
.pubkey(public_key)
|
||||||
|
.kinds(vec![
|
||||||
|
Kind::TextNote,
|
||||||
|
Kind::Repost,
|
||||||
|
Kind::Reaction,
|
||||||
|
Kind::ZapReceipt,
|
||||||
|
])
|
||||||
|
.since(Timestamp::now());
|
||||||
|
|
||||||
|
// Subscribe
|
||||||
|
print!("Subscribing for new notification...");
|
||||||
|
client.subscribe_with_id(sub_id, vec![filter], None).await;
|
||||||
|
|
||||||
|
// Get user's settings
|
||||||
|
let handle = app.clone();
|
||||||
|
// Spawn a thread to handle it
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
let window = handle.get_window("main").unwrap();
|
||||||
|
let state = window.state::<Nostr>();
|
||||||
|
let client = &state.client;
|
||||||
|
|
||||||
|
let ident = "lume:settings";
|
||||||
|
let filter = Filter::new()
|
||||||
|
.author(public_key)
|
||||||
|
.kind(Kind::ApplicationSpecificData)
|
||||||
|
.identifier(ident)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
// Connect to user's relay (NIP-65)
|
|
||||||
if let Ok(events) = client
|
if let Ok(events) = client
|
||||||
.get_events_of(
|
.get_events_of(vec![filter], Some(Duration::from_secs(5)))
|
||||||
vec![Filter::new()
|
|
||||||
.author(public_key)
|
|
||||||
.kind(Kind::RelayList)
|
|
||||||
.limit(1)],
|
|
||||||
Some(Duration::from_secs(10)),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
if let Some(event) = events.first() {
|
if let Some(event) = events.first() {
|
||||||
let relay_list = nip65::extract_relay_list(event);
|
let content = event.content();
|
||||||
for item in relay_list.into_iter() {
|
if let Ok(decrypted) = signer.nip44_decrypt(public_key, content).await {
|
||||||
let relay_url = item.0.to_string();
|
let parsed: Settings =
|
||||||
let opts = match item.1 {
|
serde_json::from_str(&decrypted).expect("Could not parse settings payload");
|
||||||
Some(val) => {
|
|
||||||
if val == &RelayMetadata::Read {
|
|
||||||
RelayOptions::new().read(true).write(false)
|
|
||||||
} else {
|
|
||||||
RelayOptions::new().write(true).read(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => RelayOptions::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add relay to relay pool
|
*state.settings.lock().unwrap() = parsed;
|
||||||
let _ = client
|
|
||||||
.add_relay_with_opts(&relay_url, opts)
|
|
||||||
.await
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Connect relay
|
|
||||||
client.connect_relay(relay_url).await.unwrap_or_default();
|
|
||||||
println!("connecting to relay: {} - {:?}", item.0, item.1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get user's contact list
|
// Run sync service
|
||||||
let contacts = client
|
let handle = app.clone();
|
||||||
.get_contact_list(Some(Duration::from_secs(10)))
|
// Spawn a thread to handle it
|
||||||
.await
|
tauri::async_runtime::spawn(async move {
|
||||||
.unwrap();
|
let window = handle.get_window("main").unwrap();
|
||||||
|
let state = window.state::<Nostr>();
|
||||||
|
let client = &state.client;
|
||||||
|
|
||||||
// Update state
|
let filter = Filter::new()
|
||||||
*state.contact_list.lock().unwrap() = contacts;
|
.pubkey(public_key)
|
||||||
|
.kinds(vec![
|
||||||
|
Kind::TextNote,
|
||||||
|
Kind::Repost,
|
||||||
|
Kind::Reaction,
|
||||||
|
Kind::ZapReceipt,
|
||||||
|
])
|
||||||
|
.limit(500);
|
||||||
|
|
||||||
// Get user's settings
|
match client.reconcile(filter, NegentropyOptions::default()).await {
|
||||||
let handle = app.clone();
|
Ok(_) => println!("Sync notification done."),
|
||||||
// Spawn a thread to handle it
|
Err(_) => println!("Sync notification failed."),
|
||||||
tauri::async_runtime::spawn(async move {
|
}
|
||||||
let window = handle.get_window("main").unwrap();
|
});
|
||||||
let state = window.state::<Nostr>();
|
|
||||||
let client = &state.client;
|
|
||||||
|
|
||||||
let ident = "lume:settings";
|
// Run notification service
|
||||||
let filter = Filter::new()
|
// Spawn a thread to handle it
|
||||||
.author(public_key)
|
tauri::async_runtime::spawn(async move {
|
||||||
.kind(Kind::ApplicationSpecificData)
|
let window = app.get_window("main").unwrap();
|
||||||
.identifier(ident)
|
let state = window.state::<Nostr>();
|
||||||
.limit(1);
|
let client = &state.client;
|
||||||
|
|
||||||
if let Ok(events) = client
|
// Handle notifications
|
||||||
.get_events_of(vec![filter], Some(Duration::from_secs(5)))
|
if client
|
||||||
.await
|
.handle_notifications(|notification| async {
|
||||||
{
|
if let RelayPoolNotification::Message { message, .. } = notification {
|
||||||
if let Some(event) = events.first() {
|
if let RelayMessage::Event {
|
||||||
let content = event.content();
|
|
||||||
if let Ok(decrypted) = signer.nip44_decrypt(public_key, content).await {
|
|
||||||
let parsed: Settings =
|
|
||||||
serde_json::from_str(&decrypted).expect("Could not parse settings payload");
|
|
||||||
|
|
||||||
*state.settings.lock().unwrap() = parsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run sync service
|
|
||||||
let handle = app.clone();
|
|
||||||
// Spawn a thread to handle it
|
|
||||||
tauri::async_runtime::spawn(async move {
|
|
||||||
let window = handle.get_window("main").unwrap();
|
|
||||||
let state = window.state::<Nostr>();
|
|
||||||
let client = &state.client;
|
|
||||||
|
|
||||||
let filter = Filter::new()
|
|
||||||
.pubkey(public_key)
|
|
||||||
.kinds(vec![
|
|
||||||
Kind::TextNote,
|
|
||||||
Kind::Repost,
|
|
||||||
Kind::Reaction,
|
|
||||||
Kind::ZapReceipt,
|
|
||||||
])
|
|
||||||
.limit(500);
|
|
||||||
|
|
||||||
match client.reconcile(filter, NegentropyOptions::default()).await {
|
|
||||||
Ok(_) => println!("Sync notification done."),
|
|
||||||
Err(_) => println!("Sync notification failed."),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run notification service
|
|
||||||
// Spawn a thread to handle it
|
|
||||||
tauri::async_runtime::spawn(async move {
|
|
||||||
println!("Starting notification service...");
|
|
||||||
|
|
||||||
let window = app.get_window("main").unwrap();
|
|
||||||
let state = window.state::<Nostr>();
|
|
||||||
let client = &state.client;
|
|
||||||
|
|
||||||
// Create a subscription for notification
|
|
||||||
let notification_id = SubscriptionId::new("notification");
|
|
||||||
let filter = Filter::new()
|
|
||||||
.pubkey(public_key)
|
|
||||||
.kinds(vec![
|
|
||||||
Kind::TextNote,
|
|
||||||
Kind::Repost,
|
|
||||||
Kind::Reaction,
|
|
||||||
Kind::ZapReceipt,
|
|
||||||
])
|
|
||||||
.since(Timestamp::now());
|
|
||||||
|
|
||||||
// Subscribe
|
|
||||||
client
|
|
||||||
.subscribe_with_id(notification_id.clone(), vec![filter], None)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Handle notifications
|
|
||||||
let _ = client
|
|
||||||
.handle_notifications(|notification| async {
|
|
||||||
if let RelayPoolNotification::Event {
|
|
||||||
subscription_id,
|
subscription_id,
|
||||||
event,
|
event,
|
||||||
..
|
} = message
|
||||||
} = notification
|
|
||||||
{
|
{
|
||||||
if subscription_id == notification_id {
|
let id = subscription_id.to_string();
|
||||||
println!("new notification: {}", event.as_json());
|
|
||||||
|
|
||||||
|
if id.starts_with("notification") {
|
||||||
if app
|
if app
|
||||||
.emit_to(
|
.emit_to(
|
||||||
EventTarget::window("panel"),
|
EventTarget::window("panel"),
|
||||||
@@ -336,78 +397,39 @@ pub async fn load_account(
|
|||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
} else if id.starts_with("event-") {
|
||||||
|
let raw = event.as_json();
|
||||||
|
let parsed = if event.kind == Kind::TextNote {
|
||||||
|
Some(parse_event(&event.content).await)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if app
|
||||||
|
.emit_to(
|
||||||
|
EventTarget::window(id),
|
||||||
|
"new_reply",
|
||||||
|
RichEvent { raw, parsed },
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
println!("Emit new notification failed.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("new event: {}", event.as_json())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
println!("new message: {}", message.as_json())
|
||||||
}
|
}
|
||||||
Ok(false)
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Err("Cancelled".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
#[specta::specta]
|
|
||||||
pub async fn connect_remote_account(uri: &str, state: State<'_, Nostr>) -> Result<String, String> {
|
|
||||||
let client = &state.client;
|
|
||||||
|
|
||||||
match NostrConnectURI::parse(uri) {
|
|
||||||
Ok(bunker_uri) => {
|
|
||||||
let app_keys = Keys::generate();
|
|
||||||
let app_secret = app_keys.secret_key().unwrap().to_string();
|
|
||||||
|
|
||||||
// Get remote user
|
|
||||||
let remote_user = bunker_uri.signer_public_key().unwrap();
|
|
||||||
let remote_npub = remote_user.to_bech32().unwrap();
|
|
||||||
|
|
||||||
match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(120), None).await {
|
|
||||||
Ok(signer) => {
|
|
||||||
let keyring = Entry::new(&remote_npub, "nostr_secret").unwrap();
|
|
||||||
let _ = keyring.set_password(&app_secret);
|
|
||||||
|
|
||||||
// Update signer
|
|
||||||
let _ = client.set_signer(Some(signer.into())).await;
|
|
||||||
|
|
||||||
Ok(remote_npub)
|
|
||||||
}
|
}
|
||||||
Err(err) => Err(err.to_string()),
|
Ok(false)
|
||||||
}
|
})
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
print!("Listing for new event...");
|
||||||
}
|
}
|
||||||
Err(err) => Err(err.to_string()),
|
});
|
||||||
}
|
|
||||||
}
|
Ok(true)
|
||||||
|
|
||||||
#[tauri::command(async)]
|
|
||||||
#[specta::specta]
|
|
||||||
pub fn get_encrypted_key(npub: &str, password: &str) -> Result<String, String> {
|
|
||||||
let keyring = Entry::new(npub, "nostr_secret").unwrap();
|
|
||||||
|
|
||||||
if let Ok(nsec) = keyring.get_password() {
|
|
||||||
let secret_key = SecretKey::from_bech32(nsec).unwrap();
|
|
||||||
let new_key = EncryptedSecretKey::new(&secret_key, password, 16, KeySecurity::Medium);
|
|
||||||
|
|
||||||
if let Ok(key) = new_key {
|
|
||||||
Ok(key.to_bech32().unwrap())
|
|
||||||
} else {
|
|
||||||
Err("Encrypt key failed".into())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err("Key not found".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command(async)]
|
|
||||||
#[specta::specta]
|
|
||||||
pub fn get_private_key(npub: &str) -> Result<String, String> {
|
|
||||||
let keyring = Entry::new(npub, "nostr_secret").unwrap();
|
|
||||||
|
|
||||||
if let Ok(nsec) = keyring.get_password() {
|
|
||||||
let secret_key = SecretKey::from_bech32(nsec).unwrap();
|
|
||||||
Ok(secret_key.to_bech32().unwrap())
|
|
||||||
} else {
|
|
||||||
Err("Key not found".into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ use std::collections::HashSet;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use linkify::LinkFinder;
|
use linkify::LinkFinder;
|
||||||
use nostr_sdk::prelude::Nip19Event;
|
|
||||||
use nostr_sdk::{Alphabet, Event, EventId, FromBech32, PublicKey, SingleLetterTag, Tag, TagKind};
|
use nostr_sdk::{Alphabet, Event, EventId, FromBech32, PublicKey, SingleLetterTag, Tag, TagKind};
|
||||||
|
use nostr_sdk::prelude::Nip19Event;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use specta::Type;
|
use specta::Type;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Type)]
|
#[derive(Debug, Clone, Serialize, Type)]
|
||||||
pub struct Meta {
|
pub struct Meta {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub images: Vec<String>,
|
pub images: Vec<String>,
|
||||||
|
|||||||
@@ -11,10 +11,9 @@
|
|||||||
"minWidth": 500,
|
"minWidth": 500,
|
||||||
"minHeight": 800,
|
"minHeight": 800,
|
||||||
"hiddenTitle": true,
|
"hiddenTitle": true,
|
||||||
"decorations": true,
|
|
||||||
"transparent": true,
|
|
||||||
"windowEffects": {
|
"windowEffects": {
|
||||||
"effects": ["windowBackground"]
|
"state": "followsWindowActiveState",
|
||||||
|
"effects": ["underWindowBackground"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -25,9 +24,8 @@
|
|||||||
"height": 500,
|
"height": 500,
|
||||||
"fullscreen": false,
|
"fullscreen": false,
|
||||||
"resizable": false,
|
"resizable": false,
|
||||||
"decorations": false,
|
|
||||||
"transparent": true,
|
|
||||||
"visible": false,
|
"visible": false,
|
||||||
|
"decorations": false,
|
||||||
"windowEffects": {
|
"windowEffects": {
|
||||||
"effects": ["popover"]
|
"effects": ["popover"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user