3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "modules/depot"]
|
|
||||||
path = modules/depot
|
|
||||||
url = https://github.com/luminous-devs/depot.git
|
|
@ -10,11 +10,14 @@
|
|||||||
"@columns/antenas": "workspace:^",
|
"@columns/antenas": "workspace:^",
|
||||||
"@columns/default": "workspace:^",
|
"@columns/default": "workspace:^",
|
||||||
"@columns/foryou": "workspace:^",
|
"@columns/foryou": "workspace:^",
|
||||||
|
"@columns/global": "workspace:^",
|
||||||
"@columns/group": "workspace:^",
|
"@columns/group": "workspace:^",
|
||||||
"@columns/hashtag": "workspace:^",
|
"@columns/hashtag": "workspace:^",
|
||||||
"@columns/thread": "workspace:^",
|
"@columns/thread": "workspace:^",
|
||||||
"@columns/timeline": "workspace:^",
|
"@columns/timeline": "workspace:^",
|
||||||
|
"@columns/trending-notes": "workspace:^",
|
||||||
"@columns/user": "workspace:^",
|
"@columns/user": "workspace:^",
|
||||||
|
"@columns/waifu": "workspace:^",
|
||||||
"@getalby/sdk": "^3.2.3",
|
"@getalby/sdk": "^3.2.3",
|
||||||
"@lume/ark": "workspace:^",
|
"@lume/ark": "workspace:^",
|
||||||
"@lume/icons": "workspace:^",
|
"@lume/icons": "workspace:^",
|
||||||
@ -35,8 +38,8 @@
|
|||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"framer-motion": "^10.18.0",
|
"framer-motion": "^11.0.3",
|
||||||
"i18next": "^23.8.0",
|
"i18next": "^23.8.1",
|
||||||
"i18next-resources-to-backend": "^1.2.0",
|
"i18next-resources-to-backend": "^1.2.0",
|
||||||
"jotai": "^2.6.3",
|
"jotai": "^2.6.3",
|
||||||
"minidenticons": "^4.2.0",
|
"minidenticons": "^4.2.0",
|
||||||
@ -50,14 +53,14 @@
|
|||||||
"react-i18next": "^14.0.1",
|
"react-i18next": "^14.0.1",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"smol-toml": "^1.1.4",
|
"smol-toml": "^1.1.4",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/node": "^20.11.6",
|
"@types/node": "^20.11.10",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.2.18",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
|
BIN
apps/desktop/public/columns/global.jpg
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
apps/desktop/public/columns/global@2x.jpg
Normal file
After Width: | Height: | Size: 448 KiB |
Before Width: | Height: | Size: 103 KiB |
BIN
apps/desktop/public/columns/trending-notes.jpg
Normal file
After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 381 KiB After Width: | Height: | Size: 381 KiB |
BIN
apps/desktop/public/columns/waifu.jpg
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
apps/desktop/public/columns/waifu@2x.jpg
Normal file
After Width: | Height: | Size: 457 KiB |
@ -36,7 +36,9 @@ export function CreateAccountScreen() {
|
|||||||
onClick={() => setMethod("self")}
|
onClick={() => setMethod("self")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-start px-4 py-3.5 bg-neutral-900 rounded-xl hover:bg-neutral-800",
|
"flex flex-col items-start px-4 py-3.5 bg-neutral-900 rounded-xl hover:bg-neutral-800",
|
||||||
method === "self" ? "ring-1 ring-teal-500" : "",
|
method === "self"
|
||||||
|
? "ring-1 ring-offset-4 ring-offset-black ring-blue-500"
|
||||||
|
: "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<p className="font-semibold">{t("signup.selfManageMethod")}</p>
|
<p className="font-semibold">{t("signup.selfManageMethod")}</p>
|
||||||
@ -49,25 +51,57 @@ export function CreateAccountScreen() {
|
|||||||
onClick={() => setMethod("managed")}
|
onClick={() => setMethod("managed")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-start px-4 py-3.5 bg-neutral-900 rounded-xl hover:bg-neutral-800",
|
"flex flex-col items-start px-4 py-3.5 bg-neutral-900 rounded-xl hover:bg-neutral-800",
|
||||||
method === "managed" ? "ring-1 ring-teal-500" : "",
|
method === "managed"
|
||||||
|
? "ring-1 ring-offset-4 ring-offset-black ring-blue-500"
|
||||||
|
: "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<p className="font-semibold">{t("signup.providerMethod")}</p>
|
<div className="inline-flex items-center gap-2">
|
||||||
|
<p className="font-semibold">{t("signup.providerMethod")}</p>
|
||||||
|
<span className="text-xs font-medium px-2.5 py-0.5 rounded-full bg-gradient-to-tr from-blue-300 via-sky-500 to-teal-200">
|
||||||
|
Beta
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<p className="text-sm font-medium text-neutral-500">
|
<p className="text-sm font-medium text-neutral-500">
|
||||||
{t("signup.providerMethodDescription")}
|
{t("signup.providerMethodDescription")}
|
||||||
</p>
|
</p>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<div className="flex flex-col gap-3">
|
||||||
type="button"
|
<button
|
||||||
onClick={next}
|
type="button"
|
||||||
className="inline-flex items-center justify-center w-full h-12 text-lg font-medium text-white bg-blue-500 rounded-xl hover:bg-blue-600"
|
onClick={next}
|
||||||
>
|
className="inline-flex items-center justify-center w-full h-12 text-lg font-medium text-white bg-blue-500 rounded-xl hover:bg-blue-600"
|
||||||
{loading ? (
|
>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
{loading ? (
|
||||||
) : (
|
<LoaderIcon className="size-5 animate-spin" />
|
||||||
t("global.continue")
|
) : (
|
||||||
)}
|
t("global.continue")
|
||||||
</button>
|
)}
|
||||||
|
</button>
|
||||||
|
{method === "managed" ? (
|
||||||
|
<div className="flex flex-col gap-1 text-sm text-neutral-500">
|
||||||
|
<p className="text-sm font-semibold text-neutral-300">
|
||||||
|
Attention:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You're chosing Managed by Provider, this feature still in
|
||||||
|
"Beta".
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Some functions still missing or not work as expected, you
|
||||||
|
shouldn't create your main account with this method
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="https://github.com/kind-0/nsecbunkerd/blob/master/OAUTH-LIKE-FLOW.md"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-blue-500"
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { Antenas } from "@columns/antenas";
|
import { Antenas } from "@columns/antenas";
|
||||||
import { Default } from "@columns/default";
|
import { Default } from "@columns/default";
|
||||||
import { ForYou } from "@columns/foryou";
|
import { ForYou } from "@columns/foryou";
|
||||||
|
import { Global } from "@columns/global";
|
||||||
import { Group } from "@columns/group";
|
import { Group } from "@columns/group";
|
||||||
import { Hashtag } from "@columns/hashtag";
|
import { Hashtag } from "@columns/hashtag";
|
||||||
import { Thread } from "@columns/thread";
|
import { Thread } from "@columns/thread";
|
||||||
import { Timeline } from "@columns/timeline";
|
import { Timeline } from "@columns/timeline";
|
||||||
|
import { TrendingNotes } from "@columns/trending-notes";
|
||||||
import { User } from "@columns/user";
|
import { User } from "@columns/user";
|
||||||
|
import { Waifu } from "@columns/waifu";
|
||||||
import { useColumnContext } from "@lume/ark";
|
import { useColumnContext } from "@lume/ark";
|
||||||
import {
|
import {
|
||||||
ArrowLeftIcon,
|
ArrowLeftIcon,
|
||||||
@ -45,6 +48,12 @@ export function HomeScreen() {
|
|||||||
return <Group key={column.id} column={column} />;
|
return <Group key={column.id} column={column} />;
|
||||||
case COL_TYPES.antenas:
|
case COL_TYPES.antenas:
|
||||||
return <Antenas key={column.id} column={column} />;
|
return <Antenas key={column.id} column={column} />;
|
||||||
|
case COL_TYPES.global:
|
||||||
|
return <Global key={column.id} column={column} />;
|
||||||
|
case COL_TYPES.trendingNotes:
|
||||||
|
return <TrendingNotes key={column.id} column={column} />;
|
||||||
|
case COL_TYPES.waifu:
|
||||||
|
return <Waifu key={column.id} column={column} />;
|
||||||
default:
|
default:
|
||||||
return <Default key={column.id} column={column} />;
|
return <Default key={column.id} column={column} />;
|
||||||
}
|
}
|
||||||
|
@ -51,11 +51,6 @@ export function ProfileSettingScreen() {
|
|||||||
|
|
||||||
let content = {
|
let content = {
|
||||||
...data,
|
...data,
|
||||||
username: data.name,
|
|
||||||
display_name: data.displayName,
|
|
||||||
bio: data.about,
|
|
||||||
image: picture,
|
|
||||||
cover: banner,
|
|
||||||
picture,
|
picture,
|
||||||
banner,
|
banner,
|
||||||
};
|
};
|
||||||
@ -140,7 +135,7 @@ export function ProfileSettingScreen() {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type={"text"}
|
type={"text"}
|
||||||
{...register("displayName")}
|
{...register("display_name")}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
className="relative h-11 w-full rounded-lg border-transparent bg-neutral-100 px-3 py-1 text-neutral-900 !outline-none backdrop-blur-xl placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:text-neutral-100"
|
className="relative h-11 w-full rounded-lg border-transparent bg-neutral-100 px-3 py-1 text-neutral-900 !outline-none backdrop-blur-xl placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:text-neutral-100"
|
||||||
/>
|
/>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"@astrojs/check": "^0.4.1",
|
"@astrojs/check": "^0.4.1",
|
||||||
"@astrojs/tailwind": "^5.1.0",
|
"@astrojs/tailwind": "^5.1.0",
|
||||||
"@fontsource/geist-mono": "^5.0.1",
|
"@fontsource/geist-mono": "^5.0.1",
|
||||||
"astro": "^4.2.4",
|
"astro": "^4.2.6",
|
||||||
"astro-seo-meta": "^4.1.0",
|
"astro-seo-meta": "^4.1.0",
|
||||||
"astro-seo-schema": "^4.0.0",
|
"astro-seo-schema": "^4.0.0",
|
||||||
"schema-dts": "^1.1.2",
|
"schema-dts": "^1.1.2",
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
|
|
||||||
index 87c23e40..bb84872e 100644
|
|
||||||
--- a/src-tauri/tauri.conf.json
|
|
||||||
+++ b/src-tauri/tauri.conf.json
|
|
||||||
@@ -50,8 +50,6 @@
|
|
||||||
"deb": {
|
|
||||||
"depends": []
|
|
||||||
},
|
|
||||||
- "externalBin": ["bin/depot"],
|
|
||||||
- "resources": ["resources/*"],
|
|
||||||
"icon": [
|
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
@ -35,15 +35,6 @@ RUN pnpm install --frozen-lockfile
|
|||||||
# Path for disable updater
|
# Path for disable updater
|
||||||
#ADD flatpak/0001-disable-tauri-updater.patch .
|
#ADD flatpak/0001-disable-tauri-updater.patch .
|
||||||
#RUN patch -p1 -t -i flatpak/0001-disable-tauri-updater.patch
|
#RUN patch -p1 -t -i flatpak/0001-disable-tauri-updater.patch
|
||||||
#ADD flatpak/0002-depot-remove.patch .
|
|
||||||
#RUN patch -p1 -t -i 0002-depot-remove.patch
|
|
||||||
|
|
||||||
# compile depot
|
|
||||||
|
|
||||||
#ADD flatpak/build-depot.sh build-depot.sh
|
|
||||||
#RUN mv flatpak/build-depot.sh build-depot.sh
|
|
||||||
#RUN sh build-depot.sh
|
|
||||||
#RUN pnpm run build:depot
|
|
||||||
|
|
||||||
#ENV VITE_FLATPAK_RESOURCE="/app/lib/lume/resources/config.toml"
|
#ENV VITE_FLATPAK_RESOURCE="/app/lib/lume/resources/config.toml"
|
||||||
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
directory_bin="/lume/src-tauri/bin"
|
|
||||||
target="x86_64-unknown-linux-gnu"
|
|
||||||
|
|
||||||
cd modules/depot
|
|
||||||
|
|
||||||
check_directory_keep=$(ls $directory_bin | grep -vE '.keep$' | wc -l)
|
|
||||||
|
|
||||||
echo $(ls $directory_bin | grep -vE '.keep$')
|
|
||||||
|
|
||||||
if [ $check_directory_keep -eq 0 ]; then
|
|
||||||
cargo build --release
|
|
||||||
mv target/release/depot "$directory_bin/depot-$target"
|
|
||||||
fi
|
|
@ -1,50 +1,78 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<component type="desktop-application">
|
<component type="desktop-application">
|
||||||
<id>nu.lume.Lume</id>
|
<id>
|
||||||
<launchable type="desktop-id">nu.lume.Lume.desktop</launchable>
|
nu.lume.Lume
|
||||||
|
</id>
|
||||||
<name>Lume</name>
|
<launchable type="desktop-id">
|
||||||
<summary>A cross-platform desktop nostr client</summary>
|
nu.lume.Lume.desktop
|
||||||
<developer_name>Ren Amamiya</developer_name>
|
</launchable>
|
||||||
|
<name>
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
Lume
|
||||||
<project_license>GPL-3.0-only</project_license>
|
</name>
|
||||||
|
<summary>
|
||||||
<url type="homepage">https://lume.nu</url>
|
A cross-platform desktop nostr client
|
||||||
<url type="bugtracker">https://github.com/luminous-devs/lume/issues</url>
|
</summary>
|
||||||
<url type="donation">https://nostree.me/npub1zfss807aer0j26mwp2la0ume0jqde3823rmu97ra6sgyyg956e0s6xw445</url>
|
<developer_name>
|
||||||
|
Ren Amamiya
|
||||||
<supports>
|
</developer_name>
|
||||||
<control>pointing</control>
|
<metadata_license>
|
||||||
<control>keyboard</control>
|
CC0-1.0
|
||||||
<control>touch</control>
|
</metadata_license>
|
||||||
</supports>
|
<project_license>
|
||||||
|
GPL-3.0-only
|
||||||
<description>
|
</project_license>
|
||||||
<p>
|
<url type="homepage">
|
||||||
Lume a cross-platform nostr client, supported nsbunker, chats and notifications
|
https://lume.nu
|
||||||
</p>
|
</url>
|
||||||
</description>
|
<url type="bugtracker">
|
||||||
|
https://github.com/luminous-devs/lume/issues
|
||||||
<custom>
|
</url>
|
||||||
<value key="Purism::form_factor">workstation</value>
|
<url type="donation">
|
||||||
<value key="Purism::form_factor">mobile</value>
|
https://nostree.me/npub1zfss807aer0j26mwp2la0ume0jqde3823rmu97ra6sgyyg956e0s6xw445
|
||||||
</custom>
|
</url>
|
||||||
<screenshots>
|
<supports>
|
||||||
<screenshot type="default">
|
<control>
|
||||||
<image> https://raw.githubusercontent.com/kogeletey/lume-nostr/feat/package-flatpak/flatpak/screenshoots/login-screen.png </image>
|
pointing
|
||||||
</screenshot>
|
</control>
|
||||||
<screenshot>
|
<control>
|
||||||
<image>https://raw.githubusercontent.com/kogeletey/lume-nostr/feat/package-flatpak/flatpak/screenshoots/collumns.png</image>
|
keyboard
|
||||||
</screenshot>
|
</control>
|
||||||
<screenshot>
|
<control>
|
||||||
<image> https://raw.githubusercontent.com/kogeletey/lume-nostr/feat/package-flatpak/flatpak/screenshoots/home-screen.png </image>
|
touch
|
||||||
</screenshot>
|
</control>
|
||||||
</screenshots>
|
</supports>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
<releases>
|
Lume a cross-platform nostr client, supported nsecbunker, chats and notifications
|
||||||
<release version="2.2.3" date="2023-12-07"/>
|
</p>
|
||||||
</releases>
|
</description>
|
||||||
<content_rating type="oars-1.1"/>
|
<custom>
|
||||||
|
<value key="Purism::form_factor">
|
||||||
|
workstation
|
||||||
|
</value>
|
||||||
|
<value key="Purism::form_factor">
|
||||||
|
mobile
|
||||||
|
</value>
|
||||||
|
</custom>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<image>
|
||||||
|
https://raw.githubusercontent.com/luminous-devs/lume/flatpak/screenshots/login-screen.png
|
||||||
|
</image>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<image>
|
||||||
|
https://raw.githubusercontent.com/luminous-devs/lume/flatpak/screenshots/collumns.png
|
||||||
|
</image>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<image>
|
||||||
|
https://raw.githubusercontent.com/luminous-devs/lume/flatpak/screenshots/home-screen.png
|
||||||
|
</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<releases>
|
||||||
|
<release version="3.0.0" date="2024-15-01" />
|
||||||
|
</releases>
|
||||||
|
<content_rating type="oars-1.1" />
|
||||||
</component>
|
</component>
|
||||||
|
@ -32,9 +32,8 @@ modules:
|
|||||||
buildsystem: simple
|
buildsystem: simple
|
||||||
build-commands:
|
build-commands:
|
||||||
- install -Dm755 bin/lume /app/bin/lume
|
- install -Dm755 bin/lume /app/bin/lume
|
||||||
# - install -Dm755 bin/depot /app/bin/depot
|
- mkdir -p /app/lib/lume/resources
|
||||||
# - mkdir -p /app/lib/lume/resources
|
- cp -r lib/lume/resources /app/lib/lume/resources
|
||||||
# - cp -r lib/lume/resources /app/lib/lume/resources
|
|
||||||
- mkdir -p /app/share/icons/hicolor/
|
- mkdir -p /app/share/icons/hicolor/
|
||||||
- cp -r share/icons/hicolor/ /app/share/icons/
|
- cp -r share/icons/hicolor/ /app/share/icons/
|
||||||
- install -Dm644 nu.lume.Lume.appdata.xml /app/share/metainfo/nu.lume.Lume.appdata.xml
|
- install -Dm644 nu.lume.Lume.appdata.xml /app/share/metainfo/nu.lume.Lume.appdata.xml
|
||||||
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 606 KiB After Width: | Height: | Size: 606 KiB |
@ -1 +0,0 @@
|
|||||||
Subproject commit 22f913f26ff365c6408b005b695200279586211f
|
|
@ -35,11 +35,11 @@
|
|||||||
"react-i18next": "^14.0.1",
|
"react-i18next": "^14.0.1",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"string-strip-html": "^13.4.5",
|
"string-strip-html": "^13.4.5",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"use-context-selector": "^1.4.1",
|
"use-context-selector": "^1.4.1",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
|
@ -62,6 +62,10 @@ export class Ark {
|
|||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getNDKEvent(event: NostrEvent) {
|
||||||
|
return new NDKEvent(this.ndk, event);
|
||||||
|
}
|
||||||
|
|
||||||
public async createEvent({
|
public async createEvent({
|
||||||
kind,
|
kind,
|
||||||
tags,
|
tags,
|
||||||
|
@ -101,7 +101,7 @@ export function ColumnHeader({
|
|||||||
className="inline-flex items-center gap-3 px-3 text-sm font-medium text-red-500 rounded-lg h-9 hover:bg-red-500 hover:text-red-50 focus:outline-none"
|
className="inline-flex items-center gap-3 px-3 text-sm font-medium text-red-500 rounded-lg h-9 hover:bg-red-500 hover:text-red-50 focus:outline-none"
|
||||||
>
|
>
|
||||||
<TrashIcon className="size-4" />
|
<TrashIcon className="size-4" />
|
||||||
{t("global.Delete")}
|
{t("global.delete")}
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
|
@ -7,12 +7,12 @@ function isImage(url: string) {
|
|||||||
|
|
||||||
export function LinkPreview({ url }: { url: string }) {
|
export function LinkPreview({ url }: { url: string }) {
|
||||||
const domain = new URL(url);
|
const domain = new URL(url);
|
||||||
const { status, data } = useOpenGraph(url);
|
const { isLoading, isError, data } = useOpenGraph(url);
|
||||||
|
|
||||||
if (status === "pending") {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-full mt-1 mb-2.5 rounded-xl overflow-hidden bg-neutral-100 dark:bg-neutral-900 border border-black/5 dark:border-white/5">
|
<div className="flex flex-col w-full mt-1 mb-2.5 rounded-xl overflow-hidden bg-neutral-100 dark:bg-neutral-900 border border-black/5 dark:border-white/5">
|
||||||
<div className="w-full h-48 animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
<div className="w-full h-48 shrink-0 animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||||
<div className="flex flex-col gap-2 px-3 py-3">
|
<div className="flex flex-col gap-2 px-3 py-3">
|
||||||
<div className="w-2/3 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
<div className="w-2/3 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||||
<div className="w-3/4 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
<div className="w-3/4 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||||
@ -37,6 +37,19 @@ export function LinkPreview({ url }: { url: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-blue-500 hover:text-blue-600"
|
||||||
|
>
|
||||||
|
{url}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={url}
|
to={url}
|
||||||
@ -50,7 +63,7 @@ export function LinkPreview({ url }: { url: string }) {
|
|||||||
alt={url}
|
alt={url}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
className="object-cover w-full h-48 bg-white rounded-t-lg"
|
className="object-cover w-full h-48 shrink-0 bg-white rounded-t-lg"
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="flex flex-col items-start p-3">
|
<div className="flex flex-col items-start p-3">
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
|
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import { User } from "../user";
|
import { User } from "../user";
|
||||||
import { useNoteContext } from "./provider";
|
import { useNoteContext } from "./provider";
|
||||||
|
|
||||||
@ -11,16 +13,47 @@ export function NoteUser({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<User.Provider pubkey={event.pubkey}>
|
<User.Provider pubkey={event.pubkey}>
|
||||||
<User.Root className={cn("flex items-center gap-3", className)}>
|
<HoverCard.Root>
|
||||||
<User.Avatar className="size-9 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
|
<User.Root className={cn("flex items-center gap-3", className)}>
|
||||||
<div className="flex h-6 flex-1 items-start justify-between gap-2">
|
<HoverCard.Trigger>
|
||||||
<User.Name className="font-semibold text-neutral-950 dark:text-neutral-50" />
|
<User.Avatar className="size-9 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
|
||||||
<User.Time
|
</HoverCard.Trigger>
|
||||||
time={event.created_at}
|
<div className="flex h-6 flex-1 items-start justify-between gap-2">
|
||||||
className="text-neutral-500 dark:text-neutral-400"
|
<User.Name className="font-semibold text-neutral-950 dark:text-neutral-50" />
|
||||||
/>
|
<User.Time
|
||||||
</div>
|
time={event.created_at}
|
||||||
</User.Root>
|
className="text-neutral-500 dark:text-neutral-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</User.Root>
|
||||||
|
<HoverCard.Portal>
|
||||||
|
<HoverCard.Content
|
||||||
|
className="data-[side=bottom]:animate-slideUpAndFade w-[300px] shadow-lg shadow-neutral-500/20 rounded-xl bg-white dark:shadow-none dark:bg-neutral-900 dark:border dark:border-neutral-800 p-5 data-[state=open]:transition-all"
|
||||||
|
sideOffset={5}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<User.Avatar className="size-11 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div>
|
||||||
|
<User.Name className="font-semibold leading-tight" />
|
||||||
|
<User.NIP05
|
||||||
|
pubkey={event.pubkey}
|
||||||
|
className="text-neutral-600 dark:text-neutral-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<User.About className="line-clamp-3" />
|
||||||
|
<Link
|
||||||
|
to={`/users/${event.pubkey}`}
|
||||||
|
className="mt-3 w-full h-8 text-sm font-medium bg-neutral-100 dark:bg-neutral-900 hover:bg-neutral-200 dark:hover:bg-neutral-800 rounded-lg inline-flex items-center justify-center"
|
||||||
|
>
|
||||||
|
View profile
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<HoverCard.Arrow className="fill-white dark:fill-neutral-800" />
|
||||||
|
</HoverCard.Content>
|
||||||
|
</HoverCard.Portal>
|
||||||
|
</HoverCard.Root>
|
||||||
</User.Provider>
|
</User.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export function UserName({ className }: { className?: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("truncate", className)}>
|
<div className={cn("max-w-[12rem] truncate", className)}>
|
||||||
{user.displayName || user.name || "Anon"}
|
{user.displayName || user.name || "Anon"}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -228,7 +228,7 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className="relative flex items-center justify-center w-screen h-screen"
|
className="relative flex items-center justify-center w-screen h-screen bg-white dark:bg-black"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-start max-w-2xl gap-1">
|
<div className="flex flex-col items-start max-w-2xl gap-1">
|
||||||
<h5 className="font-semibold uppercase">TIP:</h5>
|
<h5 className="font-semibold uppercase">TIP:</h5>
|
||||||
|
@ -12,15 +12,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { Column } from "@lume/ark";
|
import { Column } from "@lume/ark";
|
||||||
import { GroupFeedsIcon } from "@lume/icons";
|
|
||||||
import { IColumn } from "@lume/types";
|
import { IColumn } from "@lume/types";
|
||||||
|
import { EventRoute, UserRoute } from "@lume/ui";
|
||||||
import { AntenasForm } from "./components/form";
|
import { AntenasForm } from "./components/form";
|
||||||
import { HomeRoute } from "./home";
|
import { HomeRoute } from "./home";
|
||||||
import { EventRoute, UserRoute } from "@lume/ui";
|
|
||||||
|
|
||||||
export function Antenas({ column }: { column: IColumn }) {
|
export function Antenas({ column }: { column: IColumn }) {
|
||||||
const colKey = `antenas-${column.id}`;
|
const colKey = `antenas-${column.id}`;
|
||||||
@ -13,11 +12,7 @@ export function Antenas({ column }: { column: IColumn }) {
|
|||||||
<Column.Root>
|
<Column.Root>
|
||||||
{created ? (
|
{created ? (
|
||||||
<>
|
<>
|
||||||
<Column.Header
|
<Column.Header id={column.id} title={column.title} />
|
||||||
id={column.id}
|
|
||||||
title={column.title}
|
|
||||||
icon={<GroupFeedsIcon className="size-4" />}
|
|
||||||
/>
|
|
||||||
<Column.Content>
|
<Column.Content>
|
||||||
<Column.Route
|
<Column.Route
|
||||||
path="/"
|
path="/"
|
||||||
|
@ -12,15 +12,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Column, useColumnContext } from "@lume/ark";
|
import { Column, useColumnContext } from "@lume/ark";
|
||||||
import { ColumnIcon } from "@lume/icons";
|
|
||||||
import { IColumn } from "@lume/types";
|
import { IColumn } from "@lume/types";
|
||||||
import { COL_TYPES } from "@lume/utils";
|
import { COL_TYPES } from "@lume/utils";
|
||||||
|
|
||||||
@ -8,13 +7,9 @@ export function Default({ column }: { column: IColumn }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Column.Root>
|
<Column.Root>
|
||||||
<Column.Header
|
<Column.Header id={column.id} title="Add columns" />
|
||||||
id={column.id}
|
<div className="h-full flex-1 px-3 mt-3 flex flex-col gap-3 overflow-y-auto scrollbar-none">
|
||||||
title="Add columns"
|
<div className="shrink-0 h-11 flex items-center gap-5">
|
||||||
icon={<ColumnIcon className="size-4" />}
|
|
||||||
/>
|
|
||||||
<div className="h-full px-3 mt-3 flex flex-col gap-3 overflow-y-auto scrollbar-none">
|
|
||||||
<div className="h-11 flex items-center gap-5">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-9 w-max px-3 text-sm font-semibold inline-flex items-center justify-center bg-neutral-100 dark:bg-neutral-900 rounded-lg"
|
className="h-9 w-max px-3 text-sm font-semibold inline-flex items-center justify-center bg-neutral-100 dark:bg-neutral-900 rounded-lg"
|
||||||
@ -29,7 +24,7 @@ export function Default({ column }: { column: IColumn }) {
|
|||||||
Community (Coming Soon)
|
Community (Coming Soon)
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col rounded-xl overflow-hidden bg-neutral-50 dark:bg-neutral-950 ring-1 ring-neutral-100 dark:ring-neutral-900">
|
<div className="shrink-0 flex flex-col rounded-xl overflow-hidden bg-neutral-50 dark:bg-neutral-950 ring-1 ring-neutral-100 dark:ring-neutral-900">
|
||||||
<div className="h-[100px] w-full px-3 pt-3">
|
<div className="h-[100px] w-full px-3 pt-3">
|
||||||
<img
|
<img
|
||||||
src="/columns/group.jpg"
|
src="/columns/group.jpg"
|
||||||
@ -58,7 +53,7 @@ export function Default({ column }: { column: IColumn }) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col rounded-xl overflow-hidden bg-neutral-50 dark:bg-neutral-950 ring-1 ring-neutral-100 dark:ring-neutral-900">
|
<div className="shrink-0 flex flex-col rounded-xl overflow-hidden bg-neutral-50 dark:bg-neutral-950 ring-1 ring-neutral-100 dark:ring-neutral-900">
|
||||||
<div className="h-[100px] w-full px-3 pt-3">
|
<div className="h-[100px] w-full px-3 pt-3">
|
||||||
<img
|
<img
|
||||||
src="/columns/antenas.jpg"
|
src="/columns/antenas.jpg"
|
||||||
@ -87,6 +82,106 @@ export function Default({ column }: { column: IColumn }) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="shrink-0 flex flex-col rounded-xl overflow-hidden bg-neutral-50 dark:bg-neutral-950 ring-1 ring-neutral-100 dark:ring-neutral-900">
|
||||||
|
<div className="h-[100px] w-full px-3 pt-3">
|
||||||
|
<img
|
||||||
|
src="/columns/trending-notes.jpg"
|
||||||
|
srcSet="/columns/trending-notes@2x.jpg 2x"
|
||||||
|
alt="trendingNotes"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
className="w-full h-auto object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="h-16 shrink-0 px-3 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="font-semibold">Trending Notes</h1>
|
||||||
|
<p className="max-w-[18rem] truncate text-sm text-neutral-600 dark:text-neutral-500">
|
||||||
|
What is trending on Nostr?.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
addColumn({
|
||||||
|
kind: COL_TYPES.trendingNotes,
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="shrink-0 w-16 h-8 rounded-lg text-sm font-semibold bg-neutral-100 dark:bg-neutral-900 text-blue-500 hover:bg-neutral-200 dark:hover:bg-neutral-800 inline-flex items-center justify-center"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0 flex flex-col rounded-xl overflow-hidden bg-neutral-50 dark:bg-neutral-950 ring-1 ring-neutral-100 dark:ring-neutral-900">
|
||||||
|
<div className="h-[100px] w-full px-3 pt-3">
|
||||||
|
<img
|
||||||
|
src="/columns/global.jpg"
|
||||||
|
srcSet="/columns/global@2x.jpg 2x"
|
||||||
|
alt="global"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
className="w-full h-auto object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="h-16 shrink-0 px-3 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="font-semibold">Global</h1>
|
||||||
|
<p className="max-w-[18rem] truncate text-sm text-neutral-600 dark:text-neutral-500">
|
||||||
|
All things around the world.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
addColumn({
|
||||||
|
kind: COL_TYPES.global,
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="shrink-0 w-16 h-8 rounded-lg text-sm font-semibold bg-neutral-100 dark:bg-neutral-900 text-blue-500 hover:bg-neutral-200 dark:hover:bg-neutral-800 inline-flex items-center justify-center"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0 flex flex-col rounded-xl overflow-hidden bg-neutral-50 dark:bg-neutral-950 ring-1 ring-neutral-100 dark:ring-neutral-900">
|
||||||
|
<div className="h-[100px] w-full px-3 pt-3">
|
||||||
|
<img
|
||||||
|
src="/columns/waifu.jpg"
|
||||||
|
srcSet="/columns/waifu@2x.jpg 2x"
|
||||||
|
alt="waifu"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
className="w-full h-auto object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="h-16 shrink-0 px-3 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="font-semibold">Waifu</h1>
|
||||||
|
<p className="max-w-[18rem] truncate text-sm text-neutral-600 dark:text-neutral-500">
|
||||||
|
Show a random waifu image to boost your morale.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
addColumn({
|
||||||
|
kind: COL_TYPES.waifu,
|
||||||
|
title: "Waifu",
|
||||||
|
content: "",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="shrink-0 w-16 h-8 rounded-lg text-sm font-semibold bg-neutral-100 dark:bg-neutral-900 text-blue-500 hover:bg-neutral-200 dark:hover:bg-neutral-800 inline-flex items-center justify-center"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-3" />
|
||||||
</div>
|
</div>
|
||||||
</Column.Root>
|
</Column.Root>
|
||||||
);
|
);
|
||||||
|
@ -13,15 +13,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Column } from "@lume/ark";
|
import { Column } from "@lume/ark";
|
||||||
import { ForyouIcon } from "@lume/icons";
|
|
||||||
import { useStorage } from "@lume/storage";
|
import { useStorage } from "@lume/storage";
|
||||||
import { IColumn } from "@lume/types";
|
import { IColumn } from "@lume/types";
|
||||||
import { EventRoute, UserRoute } from "@lume/ui";
|
import { EventRoute, UserRoute } from "@lume/ui";
|
||||||
@ -27,12 +26,7 @@ export function ForYou({ column }: { column: IColumn }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Column.Root>
|
<Column.Root>
|
||||||
<Column.Header
|
<Column.Header id={column.id} queryKey={[colKey]} title="For You" />
|
||||||
id={column.id}
|
|
||||||
queryKey={[colKey]}
|
|
||||||
title="For You"
|
|
||||||
icon={<ForyouIcon className="size-4" />}
|
|
||||||
/>
|
|
||||||
{storage.interests?.hashtags ? (
|
{storage.interests?.hashtags ? (
|
||||||
<Column.Live
|
<Column.Live
|
||||||
filter={{
|
filter={{
|
||||||
|
26
packages/lume-column-global/package.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "@columns/global",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "./src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"@lume/ark": "workspace:^",
|
||||||
|
"@lume/icons": "workspace:^",
|
||||||
|
"@lume/ui": "workspace:^",
|
||||||
|
"@lume/utils": "workspace:^",
|
||||||
|
"@nostr-dev-kit/ndk": "^2.3.3",
|
||||||
|
"@tanstack/react-query": "^5.17.19",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-router-dom": "^6.21.3",
|
||||||
|
"sonner": "^1.4.0",
|
||||||
|
"virtua": "^0.23.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lume/tailwindcss": "workspace:^",
|
||||||
|
"@lume/tsconfig": "workspace:^",
|
||||||
|
"@lume/types": "workspace:^",
|
||||||
|
"@types/react": "^18.2.48",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
}
|
126
packages/lume-column-global/src/home.tsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { RepostNote, TextNote, useArk } from "@lume/ark";
|
||||||
|
import { ArrowRightCircleIcon, LoaderIcon, SearchIcon } from "@lume/icons";
|
||||||
|
import { EmptyFeed } from "@lume/ui";
|
||||||
|
import { FETCH_LIMIT } from "@lume/utils";
|
||||||
|
import { type NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
||||||
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { CacheSnapshot, VList, VListHandle } from "virtua";
|
||||||
|
|
||||||
|
export function HomeRoute({ colKey }: { colKey: string }) {
|
||||||
|
const ark = useArk();
|
||||||
|
const ref = useRef<VListHandle>();
|
||||||
|
const cacheKey = `${colKey}-vlist`;
|
||||||
|
|
||||||
|
const [offset, cache] = useMemo(() => {
|
||||||
|
const serialized = sessionStorage.getItem(cacheKey);
|
||||||
|
if (!serialized) return [];
|
||||||
|
return JSON.parse(serialized) as [number, CacheSnapshot];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } =
|
||||||
|
useInfiniteQuery({
|
||||||
|
queryKey: [colKey],
|
||||||
|
initialPageParam: 0,
|
||||||
|
queryFn: async ({
|
||||||
|
signal,
|
||||||
|
pageParam,
|
||||||
|
}: {
|
||||||
|
signal: AbortSignal;
|
||||||
|
pageParam: number;
|
||||||
|
}) => {
|
||||||
|
if (!ark.account.contacts.length) return [];
|
||||||
|
|
||||||
|
const events = await ark.getInfiniteEvents({
|
||||||
|
filter: {
|
||||||
|
kinds: [NDKKind.Text, NDKKind.Repost],
|
||||||
|
},
|
||||||
|
limit: FETCH_LIMIT,
|
||||||
|
pageParam,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
return events;
|
||||||
|
},
|
||||||
|
getNextPageParam: (lastPage) => {
|
||||||
|
const lastEvent = lastPage.at(-1);
|
||||||
|
if (!lastEvent) return;
|
||||||
|
return lastEvent.created_at - 1;
|
||||||
|
},
|
||||||
|
select: (data) => data?.pages.flatMap((page) => page),
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnMount: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderItem = (event: NDKEvent) => {
|
||||||
|
switch (event.kind) {
|
||||||
|
case NDKKind.Text:
|
||||||
|
return <TextNote key={event.id} event={event} className="mt-3" />;
|
||||||
|
case NDKKind.Repost:
|
||||||
|
return <RepostNote key={event.id} event={event} className="mt-3" />;
|
||||||
|
default:
|
||||||
|
return <TextNote key={event.id} event={event} className="mt-3" />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ref.current) return;
|
||||||
|
const handle = ref.current;
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
handle.scrollTo(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
sessionStorage.setItem(
|
||||||
|
cacheKey,
|
||||||
|
JSON.stringify([handle.scrollOffset, handle.cache]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full">
|
||||||
|
<VList ref={ref} cache={cache} overscan={2} className="flex-1 px-3">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="w-full flex h-16 items-center justify-center gap-2 px-3 py-1.5">
|
||||||
|
<LoaderIcon className="size-5 animate-spin" />
|
||||||
|
</div>
|
||||||
|
) : !data.length ? (
|
||||||
|
<div className="px-3 mt-3">
|
||||||
|
<EmptyFeed />
|
||||||
|
<Link
|
||||||
|
to="/suggest"
|
||||||
|
className="mt-3 w-full gap-2 inline-flex items-center justify-center text-sm font-medium rounded-lg h-9 bg-blue-500 hover:bg-blue-600 text-white"
|
||||||
|
>
|
||||||
|
<SearchIcon className="size-5" />
|
||||||
|
Find accounts to follow
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
data.map((item) => renderItem(item))
|
||||||
|
)}
|
||||||
|
<div className="flex items-center justify-center h-16">
|
||||||
|
{hasNextPage ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => fetchNextPage()}
|
||||||
|
disabled={!hasNextPage || isFetchingNextPage}
|
||||||
|
className="inline-flex items-center justify-center w-full h-12 gap-2 font-medium bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800 rounded-xl focus:outline-none"
|
||||||
|
>
|
||||||
|
{isFetchingNextPage ? (
|
||||||
|
<LoaderIcon className="size-5 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
Load more
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</VList>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
19
packages/lume-column-global/src/index.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Column } from "@lume/ark";
|
||||||
|
import { IColumn } from "@lume/types";
|
||||||
|
import { EventRoute, UserRoute } from "@lume/ui";
|
||||||
|
import { HomeRoute } from "./home";
|
||||||
|
|
||||||
|
export function Global({ column }: { column: IColumn }) {
|
||||||
|
const colKey = `global-${column.id}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column.Root>
|
||||||
|
<Column.Header id={column.id} queryKey={[colKey]} title="Global" />
|
||||||
|
<Column.Content>
|
||||||
|
<Column.Route path="/" element={<HomeRoute colKey={colKey} />} />
|
||||||
|
<Column.Route path="/events/:id" element={<EventRoute />} />
|
||||||
|
<Column.Route path="/users/:id" element={<UserRoute />} />
|
||||||
|
</Column.Content>
|
||||||
|
</Column.Root>
|
||||||
|
);
|
||||||
|
}
|
8
packages/lume-column-global/tailwind.config.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import sharedConfig from "@lume/tailwindcss";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
content: ["./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
presets: [sharedConfig],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
8
packages/lume-column-global/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@lume/tsconfig/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
@ -12,15 +12,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { Column } from "@lume/ark";
|
import { Column } from "@lume/ark";
|
||||||
import { GroupFeedsIcon } from "@lume/icons";
|
|
||||||
import { IColumn } from "@lume/types";
|
import { IColumn } from "@lume/types";
|
||||||
|
import { EventRoute, UserRoute } from "@lume/ui";
|
||||||
import { GroupForm } from "./components/form";
|
import { GroupForm } from "./components/form";
|
||||||
import { HomeRoute } from "./home";
|
import { HomeRoute } from "./home";
|
||||||
import { EventRoute, UserRoute } from "@lume/ui";
|
|
||||||
|
|
||||||
export function Group({ column }: { column: IColumn }) {
|
export function Group({ column }: { column: IColumn }) {
|
||||||
const colKey = `group-${column.id}`;
|
const colKey = `group-${column.id}`;
|
||||||
@ -13,11 +12,7 @@ export function Group({ column }: { column: IColumn }) {
|
|||||||
<Column.Root>
|
<Column.Root>
|
||||||
{created ? (
|
{created ? (
|
||||||
<>
|
<>
|
||||||
<Column.Header
|
<Column.Header id={column.id} title={column.title} />
|
||||||
id={column.id}
|
|
||||||
title={column.title}
|
|
||||||
icon={<GroupFeedsIcon className="size-4" />}
|
|
||||||
/>
|
|
||||||
<Column.Content>
|
<Column.Content>
|
||||||
<Column.Route
|
<Column.Route
|
||||||
path="/"
|
path="/"
|
||||||
|
@ -12,15 +12,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { Column } from "@lume/ark";
|
import { Column } from "@lume/ark";
|
||||||
import { HashtagIcon } from "@lume/icons";
|
|
||||||
import { IColumn } from "@lume/types";
|
import { IColumn } from "@lume/types";
|
||||||
import { HomeRoute } from "./home";
|
|
||||||
import { EventRoute, UserRoute } from "@lume/ui";
|
import { EventRoute, UserRoute } from "@lume/ui";
|
||||||
|
import { HomeRoute } from "./home";
|
||||||
|
|
||||||
export function Hashtag({ column }: { column: IColumn }) {
|
export function Hashtag({ column }: { column: IColumn }) {
|
||||||
const colKey = `hashtag-${column.id}`;
|
const colKey = `hashtag-${column.id}`;
|
||||||
@ -10,12 +9,7 @@ export function Hashtag({ column }: { column: IColumn }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Column.Root>
|
<Column.Root>
|
||||||
<Column.Header
|
<Column.Header id={column.id} queryKey={[colKey]} title={hashtag} />
|
||||||
id={column.id}
|
|
||||||
queryKey={[colKey]}
|
|
||||||
title={hashtag}
|
|
||||||
icon={<HashtagIcon className="size-4" />}
|
|
||||||
/>
|
|
||||||
<Column.Content>
|
<Column.Content>
|
||||||
<Column.Route
|
<Column.Route
|
||||||
path="/"
|
path="/"
|
||||||
|
@ -12,15 +12,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { ArrowRightCircleIcon, LoaderIcon, SearchIcon } from "@lume/icons";
|
|||||||
import { EmptyFeed } from "@lume/ui";
|
import { EmptyFeed } from "@lume/ui";
|
||||||
import { FETCH_LIMIT } from "@lume/utils";
|
import { FETCH_LIMIT } from "@lume/utils";
|
||||||
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
||||||
import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { useEffect, useMemo, useRef } from "react";
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { CacheSnapshot, VList, VListHandle } from "virtua";
|
import { CacheSnapshot, VList, VListHandle } from "virtua";
|
||||||
@ -12,7 +12,6 @@ export function HomeRoute({ colKey }: { colKey: string }) {
|
|||||||
const ark = useArk();
|
const ark = useArk();
|
||||||
const ref = useRef<VListHandle>();
|
const ref = useRef<VListHandle>();
|
||||||
const cacheKey = `${colKey}-vlist`;
|
const cacheKey = `${colKey}-vlist`;
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const [offset, cache] = useMemo(() => {
|
const [offset, cache] = useMemo(() => {
|
||||||
const serialized = sessionStorage.getItem(cacheKey);
|
const serialized = sessionStorage.getItem(cacheKey);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Column, useArk } from "@lume/ark";
|
import { Column, useArk } from "@lume/ark";
|
||||||
import { TimelineIcon } from "@lume/icons";
|
|
||||||
import { IColumn } from "@lume/types";
|
import { IColumn } from "@lume/types";
|
||||||
import { EventRoute, SuggestRoute, UserRoute } from "@lume/ui";
|
import { EventRoute, SuggestRoute, UserRoute } from "@lume/ui";
|
||||||
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
||||||
@ -26,12 +25,7 @@ export function Timeline({ column }: { column: IColumn }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Column.Root>
|
<Column.Root>
|
||||||
<Column.Header
|
<Column.Header id={column.id} queryKey={[colKey]} title="Timeline" />
|
||||||
id={column.id}
|
|
||||||
queryKey={[colKey]}
|
|
||||||
title="Timeline"
|
|
||||||
icon={<TimelineIcon className="size-5" />}
|
|
||||||
/>
|
|
||||||
{ark.account.contacts.length ? (
|
{ark.account.contacts.length ? (
|
||||||
<Column.Live
|
<Column.Live
|
||||||
filter={{
|
filter={{
|
||||||
|
26
packages/lume-column-trending-notes/package.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "@columns/trending-notes",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "./src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"@lume/ark": "workspace:^",
|
||||||
|
"@lume/icons": "workspace:^",
|
||||||
|
"@lume/ui": "workspace:^",
|
||||||
|
"@lume/utils": "workspace:^",
|
||||||
|
"@nostr-dev-kit/ndk": "^2.3.3",
|
||||||
|
"@tanstack/react-query": "^5.17.19",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-router-dom": "^6.21.3",
|
||||||
|
"sonner": "^1.4.0",
|
||||||
|
"virtua": "^0.23.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lume/tailwindcss": "workspace:^",
|
||||||
|
"@lume/tsconfig": "workspace:^",
|
||||||
|
"@lume/types": "workspace:^",
|
||||||
|
"@types/react": "^18.2.48",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
}
|
71
packages/lume-column-trending-notes/src/home.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { TextNote, useArk } from "@lume/ark";
|
||||||
|
import { LoaderIcon } from "@lume/icons";
|
||||||
|
import { type NDKEvent, type NostrEvent } from "@nostr-dev-kit/ndk";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { fetch } from "@tauri-apps/plugin-http";
|
||||||
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
|
import { CacheSnapshot, VList, VListHandle } from "virtua";
|
||||||
|
|
||||||
|
export function HomeRoute({ colKey }: { colKey: string }) {
|
||||||
|
const ark = useArk();
|
||||||
|
const ref = useRef<VListHandle>();
|
||||||
|
const cacheKey = `${colKey}-vlist`;
|
||||||
|
|
||||||
|
const [offset, cache] = useMemo(() => {
|
||||||
|
const serialized = sessionStorage.getItem(cacheKey);
|
||||||
|
if (!serialized) return [];
|
||||||
|
return JSON.parse(serialized) as [number, CacheSnapshot];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const { data, isLoading } = useQuery({
|
||||||
|
queryKey: [colKey],
|
||||||
|
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||||
|
const res = await fetch("https://api.nostr.band/v0/trending/notes", {
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res) throw new Error("Failed to fetch trending notes");
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
const events = data.notes.map((item: { event: NostrEvent }) =>
|
||||||
|
ark.getNDKEvent(item.event),
|
||||||
|
);
|
||||||
|
|
||||||
|
return events as NDKEvent[];
|
||||||
|
},
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ref.current) return;
|
||||||
|
const handle = ref.current;
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
handle.scrollTo(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
sessionStorage.setItem(
|
||||||
|
cacheKey,
|
||||||
|
JSON.stringify([handle.scrollOffset, handle.cache]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full">
|
||||||
|
<VList ref={ref} cache={cache} overscan={2} className="flex-1 px-3">
|
||||||
|
{isLoading ? (
|
||||||
|
<div className="w-full flex h-16 items-center justify-center gap-2 px-3 py-1.5">
|
||||||
|
<LoaderIcon className="size-5 animate-spin" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
data.map((item) => (
|
||||||
|
<TextNote key={item.id} event={item} className="mt-3" />
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</VList>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
23
packages/lume-column-trending-notes/src/index.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Column } from "@lume/ark";
|
||||||
|
import { IColumn } from "@lume/types";
|
||||||
|
import { EventRoute, UserRoute } from "@lume/ui";
|
||||||
|
import { HomeRoute } from "./home";
|
||||||
|
|
||||||
|
export function TrendingNotes({ column }: { column: IColumn }) {
|
||||||
|
const colKey = `trending-notes-${column.id}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column.Root>
|
||||||
|
<Column.Header
|
||||||
|
id={column.id}
|
||||||
|
queryKey={[colKey]}
|
||||||
|
title="Trending Notes"
|
||||||
|
/>
|
||||||
|
<Column.Content>
|
||||||
|
<Column.Route path="/" element={<HomeRoute colKey={colKey} />} />
|
||||||
|
<Column.Route path="/events/:id" element={<EventRoute />} />
|
||||||
|
<Column.Route path="/users/:id" element={<UserRoute />} />
|
||||||
|
</Column.Content>
|
||||||
|
</Column.Root>
|
||||||
|
);
|
||||||
|
}
|
8
packages/lume-column-trending-notes/tailwind.config.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import sharedConfig from "@lume/tailwindcss";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
content: ["./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
presets: [sharedConfig],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
8
packages/lume-column-trending-notes/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@lume/tsconfig/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
@ -12,15 +12,15 @@
|
|||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
"@lume/tsconfig": "workspace:^",
|
"@lume/tsconfig": "workspace:^",
|
||||||
"@lume/types": "workspace:^",
|
"@lume/types": "workspace:^",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"tailwind": "^4.0.0",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import { Column } from "@lume/ark";
|
import { Column } from "@lume/ark";
|
||||||
import { UserIcon } from "@lume/icons";
|
|
||||||
import { IColumn } from "@lume/types";
|
import { IColumn } from "@lume/types";
|
||||||
import { HomeRoute } from "./home";
|
|
||||||
import { EventRoute, UserRoute } from "@lume/ui";
|
import { EventRoute, UserRoute } from "@lume/ui";
|
||||||
|
import { HomeRoute } from "./home";
|
||||||
|
|
||||||
export function User({ column }: { column: IColumn }) {
|
export function User({ column }: { column: IColumn }) {
|
||||||
return (
|
return (
|
||||||
<Column.Root>
|
<Column.Root>
|
||||||
<Column.Header
|
<Column.Header id={column.id} title={column.title} />
|
||||||
id={column.id}
|
|
||||||
title={column.title}
|
|
||||||
icon={<UserIcon className="size-4" />}
|
|
||||||
/>
|
|
||||||
<Column.Content>
|
<Column.Content>
|
||||||
<Column.Route path="/" element={<HomeRoute id={column.content} />} />
|
<Column.Route path="/" element={<HomeRoute id={column.content} />} />
|
||||||
<Column.Route path="/events/:id" element={<EventRoute />} />
|
<Column.Route path="/events/:id" element={<EventRoute />} />
|
||||||
|
23
packages/lume-column-waifu/package.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@columns/waifu",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "./src/index.tsx",
|
||||||
|
"dependencies": {
|
||||||
|
"@lume/ark": "workspace:^",
|
||||||
|
"@lume/icons": "workspace:^",
|
||||||
|
"@lume/ui": "workspace:^",
|
||||||
|
"@lume/utils": "workspace:^",
|
||||||
|
"@tanstack/react-query": "^5.17.19",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-router-dom": "^6.21.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@lume/tailwindcss": "workspace:^",
|
||||||
|
"@lume/tsconfig": "workspace:^",
|
||||||
|
"@lume/types": "workspace:^",
|
||||||
|
"@types/react": "^18.2.48",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
}
|
69
packages/lume-column-waifu/src/home.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { LoaderIcon, RefreshIcon } from "@lume/icons";
|
||||||
|
import { cn } from "@lume/utils";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
export function HomeRoute({ colKey }: { colKey: string }) {
|
||||||
|
const { data, isLoading, isError, isRefetching, refetch } = useQuery({
|
||||||
|
queryKey: [colKey],
|
||||||
|
queryFn: async ({ signal }: { signal: AbortSignal }) => {
|
||||||
|
const apiUrl = "https://api.waifu.im/search";
|
||||||
|
const params = {
|
||||||
|
included_tags: "waifu",
|
||||||
|
height: ">=2000",
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryParams = new URLSearchParams(params);
|
||||||
|
const requestUrl = `${apiUrl}?${queryParams}`;
|
||||||
|
|
||||||
|
const res = await fetch(requestUrl, { signal });
|
||||||
|
|
||||||
|
if (!res.ok) throw new Error("Failed to get image url");
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data.images[0];
|
||||||
|
},
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-3 h-full flex flex-col justify-center items-center">
|
||||||
|
{isLoading ? (
|
||||||
|
<LoaderIcon className="size-5 animate-spin" />
|
||||||
|
) : isError ? (
|
||||||
|
<p className="text-center text-sm font-medium">
|
||||||
|
Failed to get image, please try again later.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<div className="relative min-h-0 flex-1 grow-0 w-full rounded-xl flex items-stretch">
|
||||||
|
<img
|
||||||
|
src={data.url}
|
||||||
|
alt={data.signature}
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
className="object-cover w-full rounded-xl ring-1 ring-black/5 dark:ring-white/5"
|
||||||
|
/>
|
||||||
|
<div className="absolute bottom-3 right-3 flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => refetch()}
|
||||||
|
className="text-sm font-medium px-2 h-7 inline-flex items-center justify-center bg-black/50 hover:bg-black/30 backdrop-blur-2xl rounded-md text-white"
|
||||||
|
>
|
||||||
|
<RefreshIcon
|
||||||
|
className={cn("size-4", isRefetching ? "animate-spin" : "")}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href={data.source}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-sm font-medium px-2 h-7 inline-flex items-center justify-center bg-black/50 hover:bg-black/30 backdrop-blur-2xl rounded-md text-white"
|
||||||
|
>
|
||||||
|
Source
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
16
packages/lume-column-waifu/src/index.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Column } from "@lume/ark";
|
||||||
|
import { IColumn } from "@lume/types";
|
||||||
|
import { HomeRoute } from "./home";
|
||||||
|
|
||||||
|
export function Waifu({ column }: { column: IColumn }) {
|
||||||
|
const colKey = `waifu-${column.id}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column.Root>
|
||||||
|
<Column.Header id={column.id} title={column.title} />
|
||||||
|
<Column.Content>
|
||||||
|
<Column.Route path="/" element={<HomeRoute colKey={colKey} />} />
|
||||||
|
</Column.Content>
|
||||||
|
</Column.Root>
|
||||||
|
);
|
||||||
|
}
|
8
packages/lume-column-waifu/tailwind.config.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import sharedConfig from "@lume/tailwindcss";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
content: ["./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
presets: [sharedConfig],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
8
packages/lume-column-waifu/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@lume/tsconfig/base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
@ -10,7 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lume/storage": "workspace:*",
|
"@lume/storage": "workspace:*",
|
||||||
"@nostr-dev-kit/ndk": "^2.3.3",
|
"@nostr-dev-kit/ndk": "^2.3.3",
|
||||||
"lru-cache": "^10.1.0",
|
"lru-cache": "^10.2.0",
|
||||||
"nostr-fetch": "^0.15.0",
|
"nostr-fetch": "^0.15.0",
|
||||||
"nostr-tools": "1.17.0",
|
"nostr-tools": "1.17.0",
|
||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
"@radix-ui/react-hover-card": "^1.0.7",
|
"@radix-ui/react-hover-card": "^1.0.7",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@radix-ui/react-popover": "^1.0.7",
|
||||||
"@tanstack/react-query": "^5.17.19",
|
"@tanstack/react-query": "^5.17.19",
|
||||||
"framer-motion": "^10.18.0",
|
"framer-motion": "^11.0.3",
|
||||||
"jotai": "^2.6.3",
|
"jotai": "^2.6.3",
|
||||||
"minidenticons": "^4.2.0",
|
"minidenticons": "^4.2.0",
|
||||||
"nostr-tools": "~1.17.0",
|
"nostr-tools": "~1.17.0",
|
||||||
@ -30,10 +30,10 @@
|
|||||||
"react-router-dom": "^6.21.3",
|
"react-router-dom": "^6.21.3",
|
||||||
"slate": "^0.101.5",
|
"slate": "^0.101.5",
|
||||||
"slate-react": "^0.101.6",
|
"slate-react": "^0.101.6",
|
||||||
"sonner": "^1.3.1",
|
"sonner": "^1.4.0",
|
||||||
"uqr": "^0.1.2",
|
"uqr": "^0.1.2",
|
||||||
"use-debounce": "^10.0.0",
|
"use-debounce": "^10.0.0",
|
||||||
"virtua": "^0.21.1"
|
"virtua": "^0.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lume/tailwindcss": "workspace:^",
|
"@lume/tailwindcss": "workspace:^",
|
||||||
|
@ -159,7 +159,7 @@ export function SearchDialog() {
|
|||||||
</Command.Group>
|
</Command.Group>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!loading ? (
|
{!loading && !events.length ? (
|
||||||
<div className="h-full flex items-center justify-center flex-col gap-3">
|
<div className="h-full flex items-center justify-center flex-col gap-3">
|
||||||
<div className="size-16 bg-blue-100 dark:bg-blue-900 rounded-full inline-flex items-center justify-center text-blue-500">
|
<div className="size-16 bg-blue-100 dark:bg-blue-900 rounded-full inline-flex items-center justify-center text-blue-500">
|
||||||
<SearchIcon className="size-6" />
|
<SearchIcon className="size-6" />
|
||||||
|
@ -57,9 +57,9 @@ export const COL_TYPES = {
|
|||||||
hashtag: 3,
|
hashtag: 3,
|
||||||
group: 4,
|
group: 4,
|
||||||
antenas: 5,
|
antenas: 5,
|
||||||
topic: 6,
|
global: 6,
|
||||||
trendingNotes: 9000,
|
trendingNotes: 9000,
|
||||||
trendingAccounts: 9001,
|
waifu: 9001,
|
||||||
foryou: 9998,
|
foryou: 9998,
|
||||||
newsfeed: 9999,
|
newsfeed: 9999,
|
||||||
};
|
};
|
||||||
|
@ -3,10 +3,10 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
export function useOpenGraph(url: string) {
|
export function useOpenGraph(url: string) {
|
||||||
const { status, data, error } = useQuery({
|
const { isLoading, isError, data } = useQuery({
|
||||||
queryKey: ["opg", url],
|
queryKey: ["opg", url],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res: Opengraph = await invoke("opengraph", { url });
|
const res: Opengraph = await invoke("fetch_opg", { url });
|
||||||
if (!res) {
|
if (!res) {
|
||||||
throw new Error("fetch preview failed");
|
throw new Error("fetch preview failed");
|
||||||
}
|
}
|
||||||
@ -19,8 +19,8 @@ export function useOpenGraph(url: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status,
|
isLoading,
|
||||||
|
isError,
|
||||||
data,
|
data,
|
||||||
error,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
1855
pnpm-lock.yaml
@ -1,25 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
|
|
||||||
let extension = ''
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
extension = '.exe'
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const host = Bun.spawn(["rustc", '-vV']);
|
|
||||||
const stdoutStr = await new Response(host.stdout).text();
|
|
||||||
const targetTriple = /host: (\S+)/g.exec(stdoutStr)[1]
|
|
||||||
|
|
||||||
if (!targetTriple) {
|
|
||||||
console.error('Failed to determine platform target triple')
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.renameSync(
|
|
||||||
`src-tauri/bins/depot${extension}`,
|
|
||||||
`src-tauri/bins/depot-${targetTriple}${extension}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
main().catch((e) => {
|
|
||||||
throw e
|
|
||||||
})
|
|
91
src-tauri/Cargo.lock
generated
@ -114,9 +114,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.4"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
@ -242,9 +242,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-io"
|
name = "async-io"
|
||||||
version = "2.3.0"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744"
|
checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock 3.3.0",
|
"async-lock 3.3.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -313,7 +313,7 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
|
checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-io 2.3.0",
|
"async-io 2.3.1",
|
||||||
"async-lock 2.8.0",
|
"async-lock 2.8.0",
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -705,9 +705,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.32"
|
version = "0.4.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a"
|
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
@ -1067,9 +1067,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.3"
|
version = "0.20.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
checksum = "da01daa5f6d41c91358398e8db4dde38e292378da1f28300b59ef4732b879454"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"darling_macro",
|
"darling_macro",
|
||||||
@ -1077,9 +1077,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_core"
|
name = "darling_core"
|
||||||
version = "0.20.3"
|
version = "0.20.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
checksum = "f44f6238b948a3c6c3073cdf53bb0c2d5e024ee27e0f35bfe9d556a12395808a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
@ -1091,9 +1091,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.20.3"
|
version = "0.20.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
checksum = "0d2d88bd93979b1feb760a6b5c531ac5ba06bd63e74894c377af02faee07b9cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1953,7 +1953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
|
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro-crate 2.0.1",
|
"proc-macro-crate 2.0.2",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2072,7 +2072,7 @@ dependencies = [
|
|||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"indexmap 2.1.0",
|
"indexmap 2.2.1",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@ -2319,9 +2319,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.1.0"
|
version = "2.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.3",
|
"hashbrown 0.14.3",
|
||||||
@ -2393,9 +2393,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
@ -2506,9 +2506,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyring"
|
name = "keyring"
|
||||||
version = "2.3.1"
|
version = "2.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85b479dcf9eae65481044dfda57af7fe2da6c1401180360f6898801fe9ed4db9"
|
checksum = "1be8bc4c6b6e9d85ecdad090fcf342a9216f53d747a537cc05e3452fd650ca46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@ -2610,9 +2610,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
version = "1.1.14"
|
version = "1.1.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "295c17e837573c8c821dbaeb3cceb3d745ad082f7572191409e69cbc1b3fd050"
|
checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@ -3599,7 +3599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
|
checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"indexmap 2.1.0",
|
"indexmap 2.2.1",
|
||||||
"line-wrap",
|
"line-wrap",
|
||||||
"quick-xml 0.31.0",
|
"quick-xml 0.31.0",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3679,9 +3679,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a"
|
checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_edit 0.20.2",
|
"toml_edit 0.20.2",
|
||||||
@ -3898,7 +3898,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.4",
|
"regex-automata 0.4.5",
|
||||||
"regex-syntax 0.8.2",
|
"regex-syntax 0.8.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3913,9 +3913,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.4"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -4204,18 +4204,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.195"
|
version = "1.0.196"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.195"
|
version = "1.0.196"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -4224,9 +4224,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.111"
|
version = "1.0.113"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.10",
|
"itoa 1.0.10",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -4275,7 +4275,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"indexmap 2.1.0",
|
"indexmap 2.2.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with_macros",
|
"serde_with_macros",
|
||||||
@ -4555,7 +4555,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap 2.1.0",
|
"indexmap 2.2.1",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -5630,7 +5630,7 @@ version = "0.19.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.1.0",
|
"indexmap 2.2.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@ -5643,7 +5643,7 @@ version = "0.20.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.1.0",
|
"indexmap 2.2.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@ -5720,9 +5720,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tray-icon"
|
name = "tray-icon"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fad962d06d2bfd9b2ab4f665fc73b175523b834b1466a294520201c5845145f8"
|
checksum = "fd26786733426b0bf632ebab410c162859a911f26c7c9e208b9e329a8ca94481"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"core-graphics 0.23.1",
|
"core-graphics 0.23.1",
|
||||||
@ -6064,15 +6064,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpage"
|
name = "webpage"
|
||||||
version = "1.6.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8598785beeb5af95abe95e7bb20c7e747d1188347080d6811d5a56d2b9a5f368"
|
checksum = "3fb86b12e58d490a99867f561ce8466ffa7b73e24d015a8e7f5bc111d4424ba2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curl",
|
"curl",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"markup5ever_rcdom",
|
"markup5ever_rcdom",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -6456,9 +6457,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.34"
|
version = "0.5.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
|
checksum = "1931d78a9c73861da0134f453bb1f790ce49b2e30eba8410b4b79bac72b46a2d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -6,7 +6,7 @@ authors = ["Ren Amamiya"]
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/luminous-devs/lume"
|
repository = "https://github.com/luminous-devs/lume"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.66"
|
rust-version = "1.70"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-alpha", features = [] }
|
tauri-build = { version = "2.0.0-alpha", features = [] }
|
||||||
@ -40,9 +40,14 @@ tauri-plugin-sql = {version="2.0.0-alpha", features = [
|
|||||||
sqlx-cli = { version = "0.7.0", default-features = false, features = [
|
sqlx-cli = { version = "0.7.0", default-features = false, features = [
|
||||||
"sqlite",
|
"sqlite",
|
||||||
] }
|
] }
|
||||||
webpage = { version = "1.6.0", features = ["serde"] }
|
webpage = { version = "2.0", features = ["serde"] }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||||
keyring = "2"
|
keyring = "2"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
keyring = { version = "2", default_features = false, features = ["linux-secret-service"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# by default Tauri runs in production mode
|
||||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||||
|
@ -1,140 +1,3 @@
|
|||||||
use keyring::Entry;
|
pub mod folder;
|
||||||
use std::process::Command;
|
pub mod opg;
|
||||||
use std::time::Duration;
|
pub mod secret;
|
||||||
use webpage::{Webpage, WebpageOptions};
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn show_in_folder(path: String) {
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
{
|
|
||||||
Command::new("explorer")
|
|
||||||
.args(["/select,", &path]) // The comma after select is not a typo
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
use std::fs::metadata;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
if path.contains(",") {
|
|
||||||
// see https://gitlab.freedesktop.org/dbus/dbus/-/issues/76
|
|
||||||
let new_path = match metadata(&path).unwrap().is_dir() {
|
|
||||||
true => path,
|
|
||||||
false => {
|
|
||||||
let mut path2 = PathBuf::from(path);
|
|
||||||
path2.pop();
|
|
||||||
path2.into_os_string().into_string().unwrap()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Command::new("xdg-open").arg(&new_path).spawn().unwrap();
|
|
||||||
} else {
|
|
||||||
Command::new("dbus-send")
|
|
||||||
.args([
|
|
||||||
"--session",
|
|
||||||
"--dest=org.freedesktop.FileManager1",
|
|
||||||
"--type=method_call",
|
|
||||||
"/org/freedesktop/FileManager1",
|
|
||||||
"org.freedesktop.FileManager1.ShowItems",
|
|
||||||
format!("array:string:file://{path}").as_str(),
|
|
||||||
"string:\"\"",
|
|
||||||
])
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
Command::new("open").args(["-R", &path]).spawn().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
|
||||||
pub struct OpenGraphResponse {
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
url: String,
|
|
||||||
image: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn fetch_opengraph(url: String) -> OpenGraphResponse {
|
|
||||||
let options = WebpageOptions {
|
|
||||||
allow_insecure: false,
|
|
||||||
max_redirections: 3,
|
|
||||||
timeout: Duration::from_secs(15),
|
|
||||||
useragent: "lume - desktop app".to_string(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = match Webpage::from_url(&url, options) {
|
|
||||||
Ok(webpage) => webpage,
|
|
||||||
Err(_) => {
|
|
||||||
return OpenGraphResponse {
|
|
||||||
title: "".to_string(),
|
|
||||||
description: "".to_string(),
|
|
||||||
url: "".to_string(),
|
|
||||||
image: "".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let html = result.html;
|
|
||||||
|
|
||||||
return OpenGraphResponse {
|
|
||||||
title: html
|
|
||||||
.opengraph
|
|
||||||
.properties
|
|
||||||
.get("title")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
description: html
|
|
||||||
.opengraph
|
|
||||||
.properties
|
|
||||||
.get("description")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
url: html
|
|
||||||
.opengraph
|
|
||||||
.properties
|
|
||||||
.get("url")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default(),
|
|
||||||
image: html
|
|
||||||
.opengraph
|
|
||||||
.images
|
|
||||||
.get(0)
|
|
||||||
.and_then(|i| Some(i.url.clone()))
|
|
||||||
.unwrap_or_default(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn opengraph(url: String) -> OpenGraphResponse {
|
|
||||||
let result = fetch_opengraph(url).await;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn secure_save(key: String, value: String) -> Result<(), ()> {
|
|
||||||
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
|
||||||
let _ = entry.set_password(&value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn secure_load(key: String) -> Result<String, String> {
|
|
||||||
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
|
||||||
if let Ok(password) = entry.get_password() {
|
|
||||||
Ok(password)
|
|
||||||
} else {
|
|
||||||
Err("not found".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn secure_remove(key: String) -> Result<(), ()> {
|
|
||||||
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
|
||||||
let _ = entry.delete_password();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
48
src-tauri/src/commands/folder.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn show_in_folder(path: String) {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
Command::new("explorer")
|
||||||
|
.args(["/select,", &path]) // The comma after select is not a typo
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
use std::fs::metadata;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
if path.contains(",") {
|
||||||
|
// see https://gitlab.freedesktop.org/dbus/dbus/-/issues/76
|
||||||
|
let new_path = match metadata(&path).unwrap().is_dir() {
|
||||||
|
true => path,
|
||||||
|
false => {
|
||||||
|
let mut path2 = PathBuf::from(path);
|
||||||
|
path2.pop();
|
||||||
|
path2.into_os_string().into_string().unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Command::new("xdg-open").arg(&new_path).spawn().unwrap();
|
||||||
|
} else {
|
||||||
|
Command::new("dbus-send")
|
||||||
|
.args([
|
||||||
|
"--session",
|
||||||
|
"--dest=org.freedesktop.FileManager1",
|
||||||
|
"--type=method_call",
|
||||||
|
"/org/freedesktop/FileManager1",
|
||||||
|
"org.freedesktop.FileManager1.ShowItems",
|
||||||
|
format!("array:string:file://{path}").as_str(),
|
||||||
|
"string:\"\"",
|
||||||
|
])
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
Command::new("open").args(["-R", &path]).spawn().unwrap();
|
||||||
|
}
|
||||||
|
}
|
50
src-tauri/src/commands/opg.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
use webpage::{Webpage, WebpageOptions};
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct OpenGraphResponse {
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
url: String,
|
||||||
|
image: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn fetch_opg(url: String) -> Result<OpenGraphResponse, ()> {
|
||||||
|
let mut options = WebpageOptions::default();
|
||||||
|
options.allow_insecure = true;
|
||||||
|
options.max_redirections = 3;
|
||||||
|
options.timeout = Duration::from_secs(15);
|
||||||
|
|
||||||
|
let info = Webpage::from_url(&url, options).expect("Failed");
|
||||||
|
let html = info.html;
|
||||||
|
|
||||||
|
let result = OpenGraphResponse {
|
||||||
|
title: html
|
||||||
|
.opengraph
|
||||||
|
.properties
|
||||||
|
.get("title")
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
description: html
|
||||||
|
.opengraph
|
||||||
|
.properties
|
||||||
|
.get("description")
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
url: html
|
||||||
|
.opengraph
|
||||||
|
.properties
|
||||||
|
.get("url")
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
image: html
|
||||||
|
.opengraph
|
||||||
|
.images
|
||||||
|
.get(0)
|
||||||
|
.and_then(|i| Some(i.url.clone()))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result.into())
|
||||||
|
}
|
25
src-tauri/src/commands/secret.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use keyring::Entry;
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn secure_save(key: String, value: String) -> Result<(), ()> {
|
||||||
|
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
||||||
|
let _ = entry.set_password(&value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn secure_load(key: String) -> Result<String, String> {
|
||||||
|
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
|
||||||
|
if let Ok(password) = entry.get_password() {
|
||||||
|
Ok(password.into())
|
||||||
|
} else {
|
||||||
|
Err("Not found".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn secure_remove(key: String) -> Result<(), ()> {
|
||||||
|
let entry = Entry::new("Lume", &key).expect("Failed to remove entry");
|
||||||
|
let _ = entry.delete_password();
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
windows_subsystem = "windows"
|
windows_subsystem = "windows"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
mod commands;
|
pub mod commands;
|
||||||
|
|
||||||
use tauri_plugin_autostart::MacosLauncher;
|
use tauri_plugin_autostart::MacosLauncher;
|
||||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||||
@ -23,14 +23,12 @@ fn main() {
|
|||||||
tauri_plugin_sql::Builder::default()
|
tauri_plugin_sql::Builder::default()
|
||||||
.add_migrations(
|
.add_migrations(
|
||||||
"sqlite:lume_v3.db",
|
"sqlite:lume_v3.db",
|
||||||
vec![
|
vec![Migration {
|
||||||
Migration {
|
version: 20230418013219,
|
||||||
version: 20230418013219,
|
description: "initial data",
|
||||||
description: "initial data",
|
sql: include_str!("../migrations/20230418013219_initial_data.sql"),
|
||||||
sql: include_str!("../migrations/20230418013219_initial_data.sql"),
|
kind: MigrationKind::Up,
|
||||||
kind: MigrationKind::Up,
|
}],
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
@ -50,11 +48,11 @@ fn main() {
|
|||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
))
|
))
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
commands::opengraph,
|
commands::secret::secure_save,
|
||||||
commands::secure_save,
|
commands::secret::secure_load,
|
||||||
commands::secure_load,
|
commands::secret::secure_remove,
|
||||||
commands::secure_remove,
|
commands::folder::show_in_folder,
|
||||||
commands::show_in_folder,
|
commands::opg::fetch_opg,
|
||||||
])
|
])
|
||||||
.run(ctx)
|
.run(ctx)
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|