Compare commits

..
13 Commits
Author SHA1 Message Date
reya 4e279f127d chore: release 2024-11-06 07:45:00 +07:00
reya 5655a8136d feat: improve dark mode and some copy 2024-11-05 19:33:47 +07:00
reya d80534c51f feat: allow user enter custom relay for relayfeeds 2024-11-05 15:04:39 +07:00
reya 0b97248fb8 chore: release 2024-11-04 10:42:46 +07:00
reya f54f448ecb feat: add tray 2024-11-04 10:40:50 +07:00
reya bd1f2b899d refactor: sync based on interval not window event 2024-11-04 09:40:33 +07:00
reya efd3c83193 fix: missing check for update 2024-11-03 13:50:52 +07:00
reya 85fa1e2359 fix: missing check for update 2024-11-03 13:50:31 +07:00
reya cd6ba5884f chore: release 2024-11-03 13:18:36 +07:00
reya bfed56ba13 fix: crash on windows 2024-11-03 13:05:24 +07:00
reya d1018ba8d1 feat: remove mica effect and support windows 10 2024-11-02 13:43:13 +07:00
reya a42542c16e fix: performance 2024-11-02 13:33:48 +07:00
reya 26a6ec954c fix: thread not reload after reply 2024-11-02 08:26:46 +07:00
36 changed files with 632 additions and 565 deletions
+1 -4
View File
@@ -4,10 +4,7 @@
"enabled": true
},
"files": {
"ignore": [
"./src/routes.gen.ts",
"./src/commands.gen.ts"
]
"ignore": ["./src/routes.gen.ts", "./src/commands.gen.ts"]
},
"linter": {
"enabled": true,
+21 -21
View File
@@ -444,8 +444,9 @@ dependencies = [
[[package]]
name = "async-wsocket"
version = "0.9.0"
source = "git+https://github.com/shadowylab/async-wsocket?rev=4d6a5b1780e65dc657ac36e5990a97c10feef072#4d6a5b1780e65dc657ac36e5990a97c10feef072"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a107e3bdbe61e8e1e1341c57241b4b2d50501127b44bd2eff13b4635ab42d35a"
dependencies = [
"async-utility",
"futures",
@@ -2987,8 +2988,7 @@ dependencies = [
[[package]]
name = "keyring-search"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fba83ff0a0efb658afeaaa6de89c7abd3ccd34333f5a36d5dae417334fcd488"
source = "git+https://github.com/reyamir/keyring-search#59d54e6a28229f09f87b9b043690ee8a1d63221e"
dependencies = [
"byteorder",
"lazy_static",
@@ -3480,8 +3480,8 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "nostr"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"aes",
"async-trait",
@@ -3511,8 +3511,8 @@ dependencies = [
[[package]]
name = "nostr-connect"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"async-trait",
"async-utility",
@@ -3525,8 +3525,8 @@ dependencies = [
[[package]]
name = "nostr-database"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"async-trait",
"flatbuffers",
@@ -3539,8 +3539,8 @@ dependencies = [
[[package]]
name = "nostr-lmdb"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"heed",
"nostr",
@@ -3552,8 +3552,8 @@ dependencies = [
[[package]]
name = "nostr-relay-pool"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"async-utility",
"async-wsocket",
@@ -3570,8 +3570,8 @@ dependencies = [
[[package]]
name = "nostr-sdk"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"async-utility",
"atomic-destructor",
@@ -3589,8 +3589,8 @@ dependencies = [
[[package]]
name = "nostr-zapper"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"async-trait",
"nostr",
@@ -3725,7 +3725,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro-crate 3.2.0",
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 2.0.79",
@@ -3733,8 +3733,8 @@ dependencies = [
[[package]]
name = "nwc"
version = "0.35.0"
source = "git+https://github.com/rust-nostr/nostr#7587fba8ee72041689aa46c1436ecfa73d75d381"
version = "0.36.0"
source = "git+https://github.com/rust-nostr/nostr#33fb0699baf3a98b22b67ee6a86317a22cac927c"
dependencies = [
"async-trait",
"async-utility",
+1 -1
View File
@@ -47,7 +47,7 @@ futures = "0.3.30"
linkify = "0.10.0"
regex = "1.10.4"
keyring = { version = "3", features = ["apple-native", "windows-native"] }
keyring-search = "1.2.0"
keyring-search = { git = "https://github.com/reyamir/keyring-search" }
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
[target.'cfg(target_os = "macos")'.dependencies]
+1 -3
View File
@@ -46,9 +46,7 @@
"decorum:allow-show-snap-overlay",
"clipboard-manager:allow-write-text",
"clipboard-manager:allow-read-text",
"dialog:allow-open",
"dialog:allow-ask",
"dialog:allow-message",
"dialog:default",
"process:allow-restart",
"process:allow-exit",
"fs:allow-read-file",
+1 -1
View File
@@ -1 +1 @@
{"window":{"identifier":"window","description":"Capability for the desktop","local":true,"windows":["*"],"permissions":["core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-close","core:window:allow-destroy","core:window:allow-set-focus","core:window:allow-center","core:window:allow-minimize","core:window:allow-maximize","core:window:allow-set-size","core:window:allow-start-dragging","core:window:allow-toggle-maximize","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-set-webview-size","core:webview:allow-set-webview-position","core:webview:allow-webview-close","core:menu:allow-new","core:menu:allow-popup","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-ask","dialog:allow-message","process:allow-restart","process:allow-exit","fs:allow-read-file","shell:allow-open","store:default","prevent-default:default","theme:default",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["macOS","windows"]}}
{"window":{"identifier":"window","description":"Capability for the desktop","local":true,"windows":["*"],"permissions":["core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-close","core:window:allow-destroy","core:window:allow-set-focus","core:window:allow-center","core:window:allow-minimize","core:window:allow-maximize","core:window:allow-set-size","core:window:allow-start-dragging","core:window:allow-toggle-maximize","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-set-webview-size","core:webview:allow-set-webview-position","core:webview:allow-webview-close","core:menu:allow-new","core:menu:allow-popup","notification:allow-is-permission-granted","notification:allow-request-permission","notification:default","os:allow-locale","os:allow-platform","os:allow-os-type","updater:default","updater:allow-check","updater:allow-download-and-install","decorum:allow-show-snap-overlay","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:default","process:allow-restart","process:allow-exit","fs:allow-read-file","shell:allow-open","store:default","prevent-default:default","theme:default",{"identifier":"http:default","allow":[{"url":"http://**/"},{"url":"https://**/"}]},{"identifier":"fs:allow-read-text-file","allow":[{"path":"$RESOURCE/locales/*"},{"path":"$RESOURCE/resources/*"}]}],"platforms":["macOS","windows"]}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

+29
View File
@@ -381,6 +381,35 @@ pub async fn get_all_local_interests(
Ok(alt_events)
}
#[tauri::command]
#[specta::specta]
pub async fn get_relay_list(id: String, state: State<'_, Nostr>) -> Result<String, String> {
let client = &state.client;
let public_key = PublicKey::parse(&id).map_err(|e| e.to_string())?;
let filter = Filter::new()
.author(public_key)
.kind(Kind::RelayList)
.limit(1);
let mut events = Events::new(&[filter.clone()]);
let mut rx = client
.stream_events(vec![filter], Some(Duration::from_secs(3)))
.await
.map_err(|e| e.to_string())?;
while let Some(event) = rx.next().await {
events.insert(event);
}
if let Some(event) = events.first() {
Ok(event.as_json())
} else {
Err("Relay list not found".into())
}
}
#[tauri::command]
#[specta::specta]
pub async fn get_all_profiles(state: State<'_, Nostr>) -> Result<Vec<Mention>, String> {
-6
View File
@@ -228,12 +228,6 @@ pub fn open_window(window: NewWindow, app_handle: tauri::AppHandle) -> Result<St
.transparent(true)
.decorations(false)
.closable(window.closable)
.effects(WindowEffectsConfig {
state: None,
effects: vec![Effect::Mica],
radius: None,
color: None,
})
.build()
.unwrap();
+156 -107
View File
@@ -18,7 +18,11 @@ use std::{
str::FromStr,
time::Duration,
};
use tauri::{path::BaseDirectory, Emitter, EventTarget, Listener, Manager};
use tauri::{
menu::{Menu, MenuItem},
path::BaseDirectory,
Emitter, EventTarget, Listener, Manager, WebviewWindowBuilder,
};
use tauri_plugin_decorum::WebviewWindowExt;
use tauri_plugin_notification::{NotificationExt, PermissionState};
use tauri_specta::{collect_commands, Builder};
@@ -30,7 +34,6 @@ pub mod common;
pub struct Nostr {
client: Client,
queue: RwLock<HashSet<PublicKey>>,
is_syncing: RwLock<bool>,
settings: RwLock<Settings>,
}
@@ -64,7 +67,7 @@ impl Default for Settings {
pub const DEFAULT_DIFFICULTY: u8 = 0;
pub const FETCH_LIMIT: usize = 50;
pub const QUEUE_DELAY: u64 = 300;
pub const QUEUE_DELAY: u64 = 150;
pub const NOTIFICATION_SUB_ID: &str = "lume_notification";
fn main() {
@@ -102,6 +105,7 @@ fn main() {
get_interest,
get_all_interests,
get_all_local_interests,
get_relay_list,
set_wallet,
load_wallet,
remove_wallet,
@@ -173,6 +177,53 @@ fn main() {
#[cfg(target_os = "macos")]
main_window.set_traffic_lights_inset(7.0, 10.0).unwrap();
// Setup tray menu item
let open_i = MenuItem::with_id(app, "open", "Open Lume", true, None::<&str>)?;
let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
// Create tray menu
let menu = Menu::with_items(app, &[&open_i, &quit_i])?;
// Get main tray
let tray = app.tray_by_id("main").unwrap();
// Set menu
tray.set_menu(Some(menu)).unwrap();
// Listen to tray events
tray.on_menu_event(|handle, event| match event.id().as_ref() {
"open" => {
if let Some(window) = handle.get_window("main") {
if window.is_visible().unwrap_or_default() {
let _ = window.set_focus();
} else {
let _ = window.show();
let _ = window.set_focus();
};
} else {
let window = WebviewWindowBuilder::from_config(
handle,
handle.config().app.windows.first().unwrap(),
)
.unwrap()
.build()
.unwrap();
// Set decoration
#[cfg(target_os = "windows")]
window.create_overlay_titlebar().unwrap();
// Restore native border
#[cfg(target_os = "macos")]
window.add_border(None);
// Set a custom inset to the traffic lights
#[cfg(target_os = "macos")]
window.set_traffic_lights_inset(7.0, 10.0).unwrap();
}
}
"quit" => {
std::process::exit(0);
}
_ => {}
});
let client = tauri::async_runtime::block_on(async move {
// Setup database
let database = NostrLMDB::open(config_dir.join("nostr"))
@@ -231,110 +282,9 @@ fn main() {
app.manage(Nostr {
client,
queue: RwLock::new(HashSet::new()),
is_syncing: RwLock::new(false),
settings: RwLock::new(Settings::default()),
});
// Trigger some actions for window events
main_window.on_window_event(move |event| match event {
tauri::WindowEvent::Focused(focused) => {
if !focused {
let handle = handle_clone_event.clone();
tauri::async_runtime::spawn(async move {
let state = handle.state::<Nostr>();
let client = &state.client;
if *state.is_syncing.read().await {
return;
}
let mut is_syncing = state.is_syncing.write().await;
// Mark sync in progress
*is_syncing = true;
let opts = SyncOptions::default();
let accounts = get_all_accounts();
if !accounts.is_empty() {
let public_keys: Vec<PublicKey> = accounts
.iter()
.filter_map(|acc| PublicKey::from_str(acc).ok())
.collect();
let filter = Filter::new().pubkeys(public_keys).kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::Reaction,
Kind::ZapReceipt,
]);
if let Ok(output) = client.sync(filter, &opts).await {
println!("Received: {}", output.received.len())
}
}
let filter = Filter::new().kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::ContactList,
Kind::FollowSet,
]);
// Get all public keys in database
if let Ok(events) = client.database().query(vec![filter]).await {
let public_keys: HashSet<PublicKey> = events
.iter()
.flat_map(|ev| ev.tags.public_keys().copied())
.collect();
let pk_vec: Vec<PublicKey> = public_keys.into_iter().collect();
for chunk in pk_vec.chunks(500) {
if chunk.is_empty() {
return;
}
let authors = chunk.to_owned();
let filter = Filter::new()
.authors(authors.clone())
.kinds(vec![
Kind::Metadata,
Kind::FollowSet,
Kind::Interests,
Kind::InterestSet,
])
.limit(1000);
if let Ok(output) = client.sync(filter, &opts).await {
println!("Received: {}", output.received.len())
}
let filter = Filter::new()
.authors(authors)
.kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::EventDeletion,
])
.limit(500);
if let Ok(output) = client.sync(filter, &opts).await {
println!("Received: {}", output.received.len())
}
}
}
// Mark sync is done
*is_syncing = false;
});
}
}
tauri::WindowEvent::Moved(_size) => {}
_ => {}
});
// Listen for request metadata
app.listen_any("request_metadata", move |event| {
let payload = event.payload();
@@ -379,7 +329,101 @@ fn main() {
});
});
// Run notification thread
// Run a thread for negentropy
tauri::async_runtime::spawn(async move {
let state = handle_clone_event.state::<Nostr>();
let client = &state.client;
// Use default sync options
let opts = SyncOptions::default();
// Set interval
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(600));
loop {
interval.tick().await;
let accounts = get_all_accounts();
let public_keys: Vec<PublicKey> = accounts
.iter()
.filter_map(|acc| PublicKey::from_str(acc).ok())
.collect();
if !public_keys.is_empty() {
// Create filter for notification
//
let filter = Filter::new().pubkeys(public_keys.clone()).kinds(vec![
Kind::TextNote,
Kind::Repost,
Kind::Reaction,
Kind::ZapReceipt,
]);
// Sync notification
//
if let Ok(output) = client.sync(filter, &opts).await {
println!("Received: {}", output.received.len())
}
// Create filter for contact list
//
let filter = Filter::new()
.authors(public_keys)
.kinds(vec![Kind::ContactList, Kind::FollowSet]);
// Sync events for contact list
//
if let Ok(events) = client.database().query(vec![filter]).await {
// Get unique public keys
let public_keys: HashSet<PublicKey> = events
.iter()
.flat_map(|ev| ev.tags.public_keys().copied())
.collect();
// Convert to vector
let public_keys: Vec<PublicKey> = public_keys.into_iter().collect();
for chunk in public_keys.chunks(1000) {
if chunk.is_empty() {
return;
}
let authors = chunk.to_owned();
// Create filter for metadata
//
let filter = Filter::new().authors(authors.clone()).kinds(vec![
Kind::Metadata,
Kind::FollowSet,
Kind::Interests,
Kind::InterestSet,
]);
// Sync metadata
//
if let Ok(output) = client.sync(filter, &opts).await {
println!("Received: {}", output.received.len())
}
// Create filter for text note
//
let filter = Filter::new()
.authors(authors)
.kinds(vec![Kind::TextNote, Kind::Repost, Kind::EventDeletion])
.limit(100);
// Sync text note
//
if let Ok(output) = client.sync(filter, &opts).await {
println!("Received: {}", output.received.len())
}
}
}
}
}
});
// Run a thread for handle notification
tauri::async_runtime::spawn(async move {
let state = handle_clone.state::<Nostr>();
let client = &state.client;
@@ -499,8 +543,13 @@ fn main() {
.plugin(tauri_plugin_upload::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_window_state::Builder::default().build())
.run(ctx)
.expect("error while running tauri application");
.build(ctx)
.expect("error while running tauri application")
.run(|_app_handle, event| {
if let tauri::RunEvent::ExitRequested { api, .. } = event {
api.prevent_exit();
}
});
}
#[cfg(debug_assertions)]
+9 -5
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "Lume",
"version": "24.11.0",
"version": "24.11.4",
"identifier": "nu.lume.Lume",
"build": {
"beforeDevCommand": "pnpm dev",
@@ -30,6 +30,13 @@
"$RESOURCE/*"
]
}
},
"trayIcon": {
"id": "main",
"iconAsTemplate": true,
"menuOnLeftClick": true,
"tooltip": "Lume",
"iconPath": "./icons/tray.png"
}
},
"bundle": {
@@ -39,10 +46,7 @@
"targets": "all",
"active": true,
"category": "SocialNetworking",
"resources": [
"resources/*",
"locales/*"
],
"resources": ["resources/*", "locales/*"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
+1 -3
View File
@@ -13,9 +13,7 @@
"hiddenTitle": true,
"transparent": true,
"windowEffects": {
"effects": [
"underWindowBackground"
]
"effects": ["underWindowBackground"]
}
}
]
+1 -6
View File
@@ -10,12 +10,7 @@
"minWidth": 480,
"minHeight": 700,
"transparent": true,
"decorations": false,
"windowEffects": {
"effects": [
"mica"
]
}
"decorations": false
}
]
}
+8
View File
@@ -248,6 +248,14 @@ async getAllLocalInterests(until: string | null) : Promise<Result<RichEvent[], s
else return { status: "error", error: e as any };
}
},
async getRelayList(id: string) : Promise<Result<string, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("get_relay_list", { id }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async setWallet(uri: string) : Promise<Result<boolean, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("set_wallet", { uri }) };
+10 -38
View File
@@ -3,11 +3,9 @@ import type {
MaybePromise,
PersistedQuery,
} from "@tanstack/query-persist-client-core";
import { ask, message, open } from "@tauri-apps/plugin-dialog";
import { open } from "@tauri-apps/plugin-dialog";
import { readFile } from "@tauri-apps/plugin-fs";
import { relaunch } from "@tauri-apps/plugin-process";
import type { Store as TauriStore } from "@tauri-apps/plugin-store";
import { check } from "@tauri-apps/plugin-updater";
import { BitcoinUnit } from "bitcoin-units";
import { type ClassValue, clsx } from "clsx";
import dayjs from "dayjs";
@@ -23,6 +21,15 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function isValidRelayUrl(string: string) {
try {
const newUrl = new URL(string);
return newUrl.protocol === "ws:" || newUrl.protocol === "wss:";
} catch (err) {
return false;
}
}
export const isImagePath = (path: string) => {
const exts = ["jpg", "jpeg", "gif", "png", "webp", "avif", "tiff"];
@@ -170,41 +177,6 @@ export function decodeZapInvoice(tags: string[][]) {
}
}
export async function checkForAppUpdates(silent: boolean) {
const update = await check();
if (!update) {
if (silent) return;
await message("You are on the latest version. Stay awesome!", {
title: "No Update Available",
kind: "info",
okLabel: "OK",
});
return;
}
if (update?.available) {
const yes = await ask(
`Update to ${update.version} is available!\n\nRelease notes: ${update.body}`,
{
title: "Update Available",
kind: "info",
okLabel: "Update",
cancelLabel: "Cancel",
},
);
if (yes) {
await update.downloadAndInstall();
await relaunch();
}
return;
}
}
export async function upload(filePath?: string) {
const allowExts = [
"png",
+1 -1
View File
@@ -160,7 +160,7 @@ function Header({
<button
type="button"
onClick={(e) => showContextMenu(e)}
className="hidden shrink-0 group-hover:inline-flex items-center justify-center size-6 border-[.5px] border-neutral-200 dark:border-neutral-800 shadow shadow-neutral-200/50 dark:shadow-none rounded-full bg-white dark:bg-black"
className="hidden shrink-0 group-hover:inline-flex items-center justify-center size-6 bg-white dark:bg-neutral-800 border-[.5px] border-neutral-200 dark:border-neutral-800 shadow shadow-neutral-200/50 dark:shadow-none rounded-full"
>
<CaretDown className="size-3" weight="bold" />
</button>
+1 -1
View File
@@ -16,7 +16,7 @@ export function NoteQuote({
<Tooltip.Trigger asChild>
<button
type="button"
onClick={() => LumeWindow.openEditor(null, event.id)}
onClick={() => LumeWindow.openEditor(undefined, event.id)}
className={cn(
"inline-flex items-center justify-center text-neutral-800 dark:text-neutral-200",
label
+29 -59
View File
@@ -13,7 +13,6 @@ import { createFileRoute } from '@tanstack/react-router'
// Import Routes
import { Route as rootRoute } from './routes/__root'
import { Route as BootstrapRelaysImport } from './routes/bootstrap-relays'
import { Route as AppImport } from './routes/_app'
import { Route as NewPostIndexImport } from './routes/new-post/index'
import { Route as AppIndexImport } from './routes/_app/index'
@@ -31,6 +30,7 @@ import { Route as ColumnsLayoutStoriesIdImport } from './routes/columns/_layout/
import { Route as ColumnsLayoutNewsfeedIdImport } from './routes/columns/_layout/newsfeed.$id'
import { Route as ColumnsLayoutInterestsIdImport } from './routes/columns/_layout/interests.$id'
import { Route as ColumnsLayoutGroupsIdImport } from './routes/columns/_layout/groups.$id'
import { Route as ColumnsLayoutEventsIdImport } from './routes/columns/_layout/events.$id'
import { Route as ColumnsLayoutCreateNewsfeedUsersImport } from './routes/columns/_layout/create-newsfeed.users'
import { Route as ColumnsLayoutCreateNewsfeedF2fImport } from './routes/columns/_layout/create-newsfeed.f2f'
@@ -75,9 +75,6 @@ const ColumnsLayoutNotificationIdLazyImport = createFileRoute(
const ColumnsLayoutLaunchpadIdLazyImport = createFileRoute(
'/columns/_layout/launchpad/$id',
)()
const ColumnsLayoutEventsIdLazyImport = createFileRoute(
'/columns/_layout/events/$id',
)()
// Create/Update Routes
@@ -93,14 +90,6 @@ const NewLazyRoute = NewLazyImport.update({
getParentRoute: () => rootRoute,
} as any).lazy(() => import('./routes/new.lazy').then((d) => d.Route))
const BootstrapRelaysRoute = BootstrapRelaysImport.update({
id: '/bootstrap-relays',
path: '/bootstrap-relays',
getParentRoute: () => rootRoute,
} as any).lazy(() =>
import('./routes/bootstrap-relays.lazy').then((d) => d.Route),
)
const AppRoute = AppImport.update({
id: '/_app',
getParentRoute: () => rootRoute,
@@ -326,14 +315,6 @@ const ColumnsLayoutLaunchpadIdLazyRoute =
import('./routes/columns/_layout/launchpad.$id.lazy').then((d) => d.Route),
)
const ColumnsLayoutEventsIdLazyRoute = ColumnsLayoutEventsIdLazyImport.update({
id: '/events/$id',
path: '/events/$id',
getParentRoute: () => ColumnsLayoutRoute,
} as any).lazy(() =>
import('./routes/columns/_layout/events.$id.lazy').then((d) => d.Route),
)
const ColumnsLayoutStoriesIdRoute = ColumnsLayoutStoriesIdImport.update({
id: '/stories/$id',
path: '/stories/$id',
@@ -366,6 +347,14 @@ const ColumnsLayoutGroupsIdRoute = ColumnsLayoutGroupsIdImport.update({
import('./routes/columns/_layout/groups.$id.lazy').then((d) => d.Route),
)
const ColumnsLayoutEventsIdRoute = ColumnsLayoutEventsIdImport.update({
id: '/events/$id',
path: '/events/$id',
getParentRoute: () => ColumnsLayoutRoute,
} as any).lazy(() =>
import('./routes/columns/_layout/events.$id.lazy').then((d) => d.Route),
)
const ColumnsLayoutCreateNewsfeedUsersRoute =
ColumnsLayoutCreateNewsfeedUsersImport.update({
id: '/users',
@@ -391,13 +380,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AppImport
parentRoute: typeof rootRoute
}
'/bootstrap-relays': {
id: '/bootstrap-relays'
path: '/bootstrap-relays'
fullPath: '/bootstrap-relays'
preLoaderRoute: typeof BootstrapRelaysImport
parentRoute: typeof rootRoute
}
'/new': {
id: '/new'
path: '/new'
@@ -580,6 +562,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ColumnsLayoutCreateNewsfeedUsersImport
parentRoute: typeof ColumnsLayoutCreateNewsfeedImport
}
'/columns/_layout/events/$id': {
id: '/columns/_layout/events/$id'
path: '/events/$id'
fullPath: '/columns/events/$id'
preLoaderRoute: typeof ColumnsLayoutEventsIdImport
parentRoute: typeof ColumnsLayoutImport
}
'/columns/_layout/groups/$id': {
id: '/columns/_layout/groups/$id'
path: '/groups/$id'
@@ -608,13 +597,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ColumnsLayoutStoriesIdImport
parentRoute: typeof ColumnsLayoutImport
}
'/columns/_layout/events/$id': {
id: '/columns/_layout/events/$id'
path: '/events/$id'
fullPath: '/columns/events/$id'
preLoaderRoute: typeof ColumnsLayoutEventsIdLazyImport
parentRoute: typeof ColumnsLayoutImport
}
'/columns/_layout/launchpad/$id': {
id: '/columns/_layout/launchpad/$id'
path: '/launchpad/$id'
@@ -691,11 +673,11 @@ interface ColumnsLayoutRouteChildren {
ColumnsLayoutOnboardingLazyRoute: typeof ColumnsLayoutOnboardingLazyRoute
ColumnsLayoutSearchLazyRoute: typeof ColumnsLayoutSearchLazyRoute
ColumnsLayoutTrendingLazyRoute: typeof ColumnsLayoutTrendingLazyRoute
ColumnsLayoutEventsIdRoute: typeof ColumnsLayoutEventsIdRoute
ColumnsLayoutGroupsIdRoute: typeof ColumnsLayoutGroupsIdRoute
ColumnsLayoutInterestsIdRoute: typeof ColumnsLayoutInterestsIdRoute
ColumnsLayoutNewsfeedIdRoute: typeof ColumnsLayoutNewsfeedIdRoute
ColumnsLayoutStoriesIdRoute: typeof ColumnsLayoutStoriesIdRoute
ColumnsLayoutEventsIdLazyRoute: typeof ColumnsLayoutEventsIdLazyRoute
ColumnsLayoutLaunchpadIdLazyRoute: typeof ColumnsLayoutLaunchpadIdLazyRoute
ColumnsLayoutNotificationIdLazyRoute: typeof ColumnsLayoutNotificationIdLazyRoute
ColumnsLayoutRelaysUrlLazyRoute: typeof ColumnsLayoutRelaysUrlLazyRoute
@@ -715,11 +697,11 @@ const ColumnsLayoutRouteChildren: ColumnsLayoutRouteChildren = {
ColumnsLayoutOnboardingLazyRoute: ColumnsLayoutOnboardingLazyRoute,
ColumnsLayoutSearchLazyRoute: ColumnsLayoutSearchLazyRoute,
ColumnsLayoutTrendingLazyRoute: ColumnsLayoutTrendingLazyRoute,
ColumnsLayoutEventsIdRoute: ColumnsLayoutEventsIdRoute,
ColumnsLayoutGroupsIdRoute: ColumnsLayoutGroupsIdRoute,
ColumnsLayoutInterestsIdRoute: ColumnsLayoutInterestsIdRoute,
ColumnsLayoutNewsfeedIdRoute: ColumnsLayoutNewsfeedIdRoute,
ColumnsLayoutStoriesIdRoute: ColumnsLayoutStoriesIdRoute,
ColumnsLayoutEventsIdLazyRoute: ColumnsLayoutEventsIdLazyRoute,
ColumnsLayoutLaunchpadIdLazyRoute: ColumnsLayoutLaunchpadIdLazyRoute,
ColumnsLayoutNotificationIdLazyRoute: ColumnsLayoutNotificationIdLazyRoute,
ColumnsLayoutRelaysUrlLazyRoute: ColumnsLayoutRelaysUrlLazyRoute,
@@ -760,7 +742,6 @@ const SettingsIdLazyRouteWithChildren = SettingsIdLazyRoute._addFileChildren(
export interface FileRoutesByFullPath {
'': typeof AppRouteWithChildren
'/bootstrap-relays': typeof BootstrapRelaysRoute
'/new': typeof NewLazyRoute
'/$id/set-group': typeof IdSetGroupRoute
'/$id/set-interest': typeof IdSetInterestRoute
@@ -786,11 +767,11 @@ export interface FileRoutesByFullPath {
'/columns/trending': typeof ColumnsLayoutTrendingLazyRoute
'/columns/create-newsfeed/f2f': typeof ColumnsLayoutCreateNewsfeedF2fRoute
'/columns/create-newsfeed/users': typeof ColumnsLayoutCreateNewsfeedUsersRoute
'/columns/events/$id': typeof ColumnsLayoutEventsIdRoute
'/columns/groups/$id': typeof ColumnsLayoutGroupsIdRoute
'/columns/interests/$id': typeof ColumnsLayoutInterestsIdRoute
'/columns/newsfeed/$id': typeof ColumnsLayoutNewsfeedIdRoute
'/columns/stories/$id': typeof ColumnsLayoutStoriesIdRoute
'/columns/events/$id': typeof ColumnsLayoutEventsIdLazyRoute
'/columns/launchpad/$id': typeof ColumnsLayoutLaunchpadIdLazyRoute
'/columns/notification/$id': typeof ColumnsLayoutNotificationIdLazyRoute
'/columns/relays/$url': typeof ColumnsLayoutRelaysUrlLazyRoute
@@ -799,7 +780,6 @@ export interface FileRoutesByFullPath {
}
export interface FileRoutesByTo {
'/bootstrap-relays': typeof BootstrapRelaysRoute
'/new': typeof NewLazyRoute
'/$id/set-group': typeof IdSetGroupRoute
'/$id/set-interest': typeof IdSetInterestRoute
@@ -825,11 +805,11 @@ export interface FileRoutesByTo {
'/columns/trending': typeof ColumnsLayoutTrendingLazyRoute
'/columns/create-newsfeed/f2f': typeof ColumnsLayoutCreateNewsfeedF2fRoute
'/columns/create-newsfeed/users': typeof ColumnsLayoutCreateNewsfeedUsersRoute
'/columns/events/$id': typeof ColumnsLayoutEventsIdRoute
'/columns/groups/$id': typeof ColumnsLayoutGroupsIdRoute
'/columns/interests/$id': typeof ColumnsLayoutInterestsIdRoute
'/columns/newsfeed/$id': typeof ColumnsLayoutNewsfeedIdRoute
'/columns/stories/$id': typeof ColumnsLayoutStoriesIdRoute
'/columns/events/$id': typeof ColumnsLayoutEventsIdLazyRoute
'/columns/launchpad/$id': typeof ColumnsLayoutLaunchpadIdLazyRoute
'/columns/notification/$id': typeof ColumnsLayoutNotificationIdLazyRoute
'/columns/relays/$url': typeof ColumnsLayoutRelaysUrlLazyRoute
@@ -840,7 +820,6 @@ export interface FileRoutesByTo {
export interface FileRoutesById {
__root__: typeof rootRoute
'/_app': typeof AppRouteWithChildren
'/bootstrap-relays': typeof BootstrapRelaysRoute
'/new': typeof NewLazyRoute
'/$id/set-group': typeof IdSetGroupRoute
'/$id/set-interest': typeof IdSetInterestRoute
@@ -867,11 +846,11 @@ export interface FileRoutesById {
'/columns/_layout/trending': typeof ColumnsLayoutTrendingLazyRoute
'/columns/_layout/create-newsfeed/f2f': typeof ColumnsLayoutCreateNewsfeedF2fRoute
'/columns/_layout/create-newsfeed/users': typeof ColumnsLayoutCreateNewsfeedUsersRoute
'/columns/_layout/events/$id': typeof ColumnsLayoutEventsIdRoute
'/columns/_layout/groups/$id': typeof ColumnsLayoutGroupsIdRoute
'/columns/_layout/interests/$id': typeof ColumnsLayoutInterestsIdRoute
'/columns/_layout/newsfeed/$id': typeof ColumnsLayoutNewsfeedIdRoute
'/columns/_layout/stories/$id': typeof ColumnsLayoutStoriesIdRoute
'/columns/_layout/events/$id': typeof ColumnsLayoutEventsIdLazyRoute
'/columns/_layout/launchpad/$id': typeof ColumnsLayoutLaunchpadIdLazyRoute
'/columns/_layout/notification/$id': typeof ColumnsLayoutNotificationIdLazyRoute
'/columns/_layout/relays/$url': typeof ColumnsLayoutRelaysUrlLazyRoute
@@ -883,7 +862,6 @@ export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths:
| ''
| '/bootstrap-relays'
| '/new'
| '/$id/set-group'
| '/$id/set-interest'
@@ -909,11 +887,11 @@ export interface FileRouteTypes {
| '/columns/trending'
| '/columns/create-newsfeed/f2f'
| '/columns/create-newsfeed/users'
| '/columns/events/$id'
| '/columns/groups/$id'
| '/columns/interests/$id'
| '/columns/newsfeed/$id'
| '/columns/stories/$id'
| '/columns/events/$id'
| '/columns/launchpad/$id'
| '/columns/notification/$id'
| '/columns/relays/$url'
@@ -921,7 +899,6 @@ export interface FileRouteTypes {
| '/columns/users/$id'
fileRoutesByTo: FileRoutesByTo
to:
| '/bootstrap-relays'
| '/new'
| '/$id/set-group'
| '/$id/set-interest'
@@ -947,11 +924,11 @@ export interface FileRouteTypes {
| '/columns/trending'
| '/columns/create-newsfeed/f2f'
| '/columns/create-newsfeed/users'
| '/columns/events/$id'
| '/columns/groups/$id'
| '/columns/interests/$id'
| '/columns/newsfeed/$id'
| '/columns/stories/$id'
| '/columns/events/$id'
| '/columns/launchpad/$id'
| '/columns/notification/$id'
| '/columns/relays/$url'
@@ -960,7 +937,6 @@ export interface FileRouteTypes {
id:
| '__root__'
| '/_app'
| '/bootstrap-relays'
| '/new'
| '/$id/set-group'
| '/$id/set-interest'
@@ -987,11 +963,11 @@ export interface FileRouteTypes {
| '/columns/_layout/trending'
| '/columns/_layout/create-newsfeed/f2f'
| '/columns/_layout/create-newsfeed/users'
| '/columns/_layout/events/$id'
| '/columns/_layout/groups/$id'
| '/columns/_layout/interests/$id'
| '/columns/_layout/newsfeed/$id'
| '/columns/_layout/stories/$id'
| '/columns/_layout/events/$id'
| '/columns/_layout/launchpad/$id'
| '/columns/_layout/notification/$id'
| '/columns/_layout/relays/$url'
@@ -1002,7 +978,6 @@ export interface FileRouteTypes {
export interface RootRouteChildren {
AppRoute: typeof AppRouteWithChildren
BootstrapRelaysRoute: typeof BootstrapRelaysRoute
NewLazyRoute: typeof NewLazyRoute
IdSetGroupRoute: typeof IdSetGroupRoute
IdSetInterestRoute: typeof IdSetInterestRoute
@@ -1018,7 +993,6 @@ export interface RootRouteChildren {
const rootRouteChildren: RootRouteChildren = {
AppRoute: AppRouteWithChildren,
BootstrapRelaysRoute: BootstrapRelaysRoute,
NewLazyRoute: NewLazyRoute,
IdSetGroupRoute: IdSetGroupRoute,
IdSetInterestRoute: IdSetInterestRoute,
@@ -1045,7 +1019,6 @@ export const routeTree = rootRoute
"filePath": "__root.tsx",
"children": [
"/_app",
"/bootstrap-relays",
"/new",
"/$id/set-group",
"/$id/set-interest",
@@ -1065,9 +1038,6 @@ export const routeTree = rootRoute
"/_app/"
]
},
"/bootstrap-relays": {
"filePath": "bootstrap-relays.tsx"
},
"/new": {
"filePath": "new.lazy.tsx"
},
@@ -1098,11 +1068,11 @@ export const routeTree = rootRoute
"/columns/_layout/onboarding",
"/columns/_layout/search",
"/columns/_layout/trending",
"/columns/_layout/events/$id",
"/columns/_layout/groups/$id",
"/columns/_layout/interests/$id",
"/columns/_layout/newsfeed/$id",
"/columns/_layout/stories/$id",
"/columns/_layout/events/$id",
"/columns/_layout/launchpad/$id",
"/columns/_layout/notification/$id",
"/columns/_layout/relays/$url",
@@ -1193,6 +1163,10 @@ export const routeTree = rootRoute
"filePath": "columns/_layout/create-newsfeed.users.tsx",
"parent": "/columns/_layout/create-newsfeed"
},
"/columns/_layout/events/$id": {
"filePath": "columns/_layout/events.$id.tsx",
"parent": "/columns/_layout"
},
"/columns/_layout/groups/$id": {
"filePath": "columns/_layout/groups.$id.tsx",
"parent": "/columns/_layout"
@@ -1209,10 +1183,6 @@ export const routeTree = rootRoute
"filePath": "columns/_layout/stories.$id.tsx",
"parent": "/columns/_layout"
},
"/columns/_layout/events/$id": {
"filePath": "columns/_layout/events.$id.lazy.tsx",
"parent": "/columns/_layout"
},
"/columns/_layout/launchpad/$id": {
"filePath": "columns/_layout/launchpad.$id.lazy.tsx",
"parent": "/columns/_layout"
+12 -2
View File
@@ -1,4 +1,5 @@
import { commands } from "@/commands.gen";
import { cn } from "@/commons";
import { Spinner } from "@/components";
import type { Metadata, NostrEvent } from "@/types";
import { type QueryClient, queryOptions } from "@tanstack/react-query";
@@ -36,7 +37,7 @@ export const Route = createRootRouteWithContext<RouterContext>()({
});
function Screen() {
const { queryClient } = Route.useRouteContext();
const { queryClient, platform } = Route.useRouteContext();
useEffect(() => {
const unlisten = getCurrentWindow().listen<string>(
@@ -60,7 +61,16 @@ function Screen() {
};
}, []);
return <Outlet />;
return (
<div
className={cn(
"size-full",
platform === "windows" ? "bg-neutral-100 dark:bg-neutral-900" : "",
)}
>
<Outlet />
</div>
);
}
function Pending() {
+10 -1
View File
@@ -22,10 +22,19 @@ export const Route = createLazyFileRoute("/_app")({
});
function Layout() {
const { platform } = Route.useRouteContext();
return (
<div className="flex flex-col w-screen h-screen">
<Topbar />
<div className="flex-1 bg-neutral-100 dark:bg-neutral-900 border-t-[.5px] border-black/20 dark:border-white/30">
<div
className={cn(
"flex-1 bg-neutral-100 dark:bg-neutral-900 border-t-[.5px]",
platform === "windows"
? "border-black/10 dark:border-white/10"
: "border-black/20 dark:border-white/30",
)}
>
<Outlet />
</div>
</div>
+38 -11
View File
@@ -1,14 +1,41 @@
import { commands } from '@/commands.gen'
import { createFileRoute, redirect } from '@tanstack/react-router'
import { commands } from "@/commands.gen";
import { createFileRoute, redirect } from "@tanstack/react-router";
import { ask } from "@tauri-apps/plugin-dialog";
import { relaunch } from "@tauri-apps/plugin-process";
import { check } from "@tauri-apps/plugin-updater";
export const Route = createFileRoute('/_app')({
beforeLoad: async () => {
const accounts = await commands.getAccounts()
async function checkForAppUpdates() {
const update = await check();
if (!accounts.length) {
throw redirect({ to: '/new', replace: true })
}
if (update?.available) {
const yes = await ask(
`Update to ${update.version} is available!\n\nRelease notes: ${update.body}`,
{
title: "Update Available",
kind: "info",
okLabel: "Update",
cancelLabel: "Cancel",
},
);
return { accounts }
},
})
if (yes) {
await update.downloadAndInstall();
await relaunch();
}
return;
}
}
export const Route = createFileRoute("/_app")({
beforeLoad: async () => {
await checkForAppUpdates();
const accounts = await commands.getAccounts();
if (!accounts.length) {
throw redirect({ to: "/new", replace: true });
}
return { accounts };
},
});
-159
View File
@@ -1,159 +0,0 @@
import { commands } from "@/commands.gen";
import { GoBack } from "@/components";
import { Frame } from "@/components/frame";
import { Spinner } from "@/components/spinner";
import { ArrowLeft, Plus, X } from "@phosphor-icons/react";
import { createLazyFileRoute } from "@tanstack/react-router";
import { message } from "@tauri-apps/plugin-dialog";
import { relaunch } from "@tauri-apps/plugin-process";
import { useEffect, useState, useTransition } from "react";
export const Route = createLazyFileRoute("/bootstrap-relays")({
component: Screen,
});
function Screen() {
const bootstrapRelays = Route.useLoaderData();
const [relays, setRelays] = useState<string[]>([]);
const [newRelay, setNewRelay] = useState("");
const [isPending, startTransition] = useTransition();
const add = () => {
try {
let url = newRelay;
if (!url.startsWith("wss://")) {
url = `wss://${url}`;
}
// Validate URL
const relay = new URL(url);
// Update
setRelays((prev) => [...prev, relay.toString()]);
setNewRelay("");
} catch {
message("URL is not valid.", { kind: "error" });
}
};
const remove = (relay: string) => {
setRelays((prev) => prev.filter((item) => item !== relay));
};
const submit = () => {
startTransition(async () => {
if (!relays.length) {
await message("You need to add at least 1 relay", {
title: "Manage Relays",
kind: "info",
});
return;
}
const merged = relays.join("\r\n");
const res = await commands.setBootstrapRelays(merged);
if (res.status === "ok") {
return await relaunch();
} else {
await message(res.error, {
title: "Manage Relays",
kind: "error",
});
return;
}
});
};
useEffect(() => {
setRelays(bootstrapRelays);
}, [bootstrapRelays]);
return (
<div
data-tauri-drag-region
className="relative size-full flex items-center justify-center"
>
<div className="w-[320px] flex flex-col gap-8">
<div className="flex flex-col gap-1 text-center">
<h1 className="leading-tight text-xl font-semibold">Manage Relays</h1>
<p className="text-sm text-neutral-700 dark:text-neutral-300">
The default relays that Lume will connected.
</p>
</div>
<div className="flex flex-col gap-3">
<Frame
className="flex flex-col gap-3 p-3 rounded-xl overflow-hidden"
shadow
>
<div className="flex gap-2">
<input
name="relay"
type="text"
placeholder="ex: relay.nostr.net, ..."
value={newRelay}
onChange={(e) => setNewRelay(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") add();
}}
className="flex-1 px-3 rounded-lg h-9 bg-transparent border border-neutral-200 dark:border-neutral-800 focus:border-blue-500 focus:outline-none placeholder:text-neutral-400 dark:placeholder:text-neutral-600"
/>
<button
type="submit"
onClick={() => add()}
className="inline-flex items-center justify-center size-9 rounded-lg bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
<Plus className="size-5" />
</button>
</div>
<div className="flex flex-col gap-2">
{relays.map((relay) => (
<div
key={relay}
className="flex items-center justify-between h-9 px-2 rounded-lg bg-neutral-100 dark:bg-neutral-900"
>
<div className="text-sm font-medium">{relay}</div>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => remove(relay)}
className="inline-flex items-center justify-center rounded-md size-7 text-neutral-700 dark:text-white/20 hover:bg-black/10 dark:hover:bg-white/10"
>
<X className="size-3" />
</button>
</div>
</div>
))}
</div>
<div className="text-xs text-neutral-600 dark:text-neutral-400">
<p>
Lume is heavily depend on Negentropy for syncing data. You need
to use at least 1 relay that support Negentropy. If you not
sure, you can keep using the default relay list.
</p>
</div>
</Frame>
<div className="flex flex-col items-center gap-1">
<button
type="button"
onClick={() => submit()}
disabled={isPending || !relays.length}
className="inline-flex items-center justify-center w-full h-9 text-sm font-semibold text-white bg-blue-500 rounded-lg shrink-0 hover:bg-blue-600 disabled:opacity-50"
>
{isPending ? <Spinner /> : "Save & Restart"}
</button>
<span className="mt-2 w-full text-sm text-neutral-600 dark:text-neutral-400 inline-flex items-center justify-center">
Lume will relaunch after saving.
</span>
</div>
</div>
</div>
<GoBack className="fixed top-11 left-2 flex items-center gap-1.5 text-sm font-medium">
<ArrowLeft className="size-5" />
Back
</GoBack>
</div>
);
}
-14
View File
@@ -1,14 +0,0 @@
import { commands } from "@/commands.gen";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/bootstrap-relays")({
loader: async () => {
const res = await commands.getBootstrapRelays();
if (res.status === "ok") {
return res.data.map((item) => item.replace(",", ""));
} else {
throw new Error(res.error);
}
},
});
@@ -93,7 +93,7 @@ function Screen() {
data?.map((item) => (
<div
key={item}
className="w-full p-2 mb-2 overflow-hidden bg-white rounded-lg h-max dark:bg-black/20shadow-primary dark:ring-1 ring-neutral-800/50"
className="w-full p-2 mb-2 overflow-hidden bg-white rounded-lg h-max dark:bg-black/20 shadow-primary dark:ring-1 ring-neutral-800/50"
>
<User.Provider pubkey={item}>
<User.Root>
@@ -143,12 +143,14 @@ function Screen() {
</p>
</div>
) : isError ? (
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-sm text-center">{error?.message ?? "Error"}</p>
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
{error?.message ?? "Error"}
</p>
</div>
) : !data?.length ? (
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-sm text-center">
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
Nothing to show yet, you can use Lume more and comeback lack to
see new events.
</p>
@@ -156,6 +158,13 @@ function Screen() {
) : (
data?.map((item) => renderItem(item))
)}
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
Lume running sync in the background,
<br />
the more you use the more event you see.
</p>
</div>
{hasNextPage ? (
<button
type="button"
@@ -60,9 +60,9 @@ function Screen() {
const renderItem = useCallback(
(item: NostrEvent) => {
const users = item.tags.filter((tag) => tag[0] === "p");
const name =
item.tags.find((tag) => tag[0] === "title")?.[1] || "Unnamed";
const label = item.tags.find((tag) => tag[0] === "d")?.[1] || nanoid();
return (
@@ -71,32 +71,24 @@ function Screen() {
className="mb-3 group flex flex-col rounded-xl overflow-hidden bg-white dark:bg-neutral-800/50 shadow-lg shadow-primary dark:ring-1 dark:ring-neutral-800"
>
<div className="px-2 pt-2">
<ScrollArea.Root
type={"scroll"}
scrollHideDelay={300}
className="overflow-hidden size-full"
>
<ScrollArea.Viewport className="p-3 h-16 bg-neutral-100 dark:bg-neutral-800 rounded-lg">
<div className="flex flex-wrap items-center justify-center gap-2">
{item.tags
.filter((tag) => tag[0] === "p")
.map((tag) => (
<User.Provider key={tag[1]} pubkey={tag[1]}>
<User.Root>
<User.Avatar className="size-8 rounded-full" />
</User.Root>
</User.Provider>
))}
</div>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
orientation="vertical"
>
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
</ScrollArea.Scrollbar>
<ScrollArea.Corner className="bg-transparent" />
</ScrollArea.Root>
<div className="p-3 h-16 bg-neutral-100 dark:bg-neutral-800 rounded-lg">
<div className="flex flex-wrap items-center justify-center gap-2">
{users.slice(0, 5).map((tag) => (
<User.Provider key={tag[1]} pubkey={tag[1]}>
<User.Root>
<User.Avatar className="size-8 rounded-full" />
</User.Root>
</User.Provider>
))}
{users.length > 5 ? (
<div className="size-8 rounded-full inline-flex items-center justify-center bg-neutral-300 dark:bg-neutral-700">
<p className="truncate leading-tight text-[8px] font-medium">
+{users.length - 5}
</p>
</div>
) : null}
</div>
</div>
</div>
<div className="p-2 flex items-center justify-between">
<div className="inline-flex items-center gap-2">
@@ -145,12 +137,14 @@ function Screen() {
</p>
</div>
) : isError ? (
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-sm text-center">{error?.message ?? "Error"}</p>
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
{error?.message ?? "Error"}
</p>
</div>
) : !data?.length ? (
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-sm text-center">
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
Nothing to show yet, you can use Lume more and comeback lack to
see new events.
</p>
@@ -158,6 +152,13 @@ function Screen() {
) : (
data?.map((item) => renderItem(item))
)}
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
Lume running sync in the background,
<br />
the more you use the more event you see.
</p>
</div>
{hasNextPage ? (
<button
type="button"
@@ -115,12 +115,14 @@ function Screen() {
</p>
</div>
) : isError ? (
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-sm text-center">{error?.message ?? "Error"}</p>
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
{error?.message ?? "Error"}
</p>
</div>
) : !data?.length ? (
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-sm text-center">
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
Nothing to show yet, you can use Lume more and comeback lack to
see new events.
</p>
@@ -128,6 +130,13 @@ function Screen() {
) : (
data?.map((item) => renderItem(item))
)}
<div className="mb-3 flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/20">
<p className="text-xs text-center px-4 text-neutral-500 dark:text-neutral-400">
Lume running sync in the background,
<br />
the more you use the more event you see.
</p>
</div>
{hasNextPage ? (
<button
type="button"
+10 -2
View File
@@ -175,7 +175,6 @@ function ReplyList() {
return events.filter((ev) => !removeQueues.has(ev.id));
},
refetchOnWindowFocus: false,
});
useEffect(() => {
@@ -183,7 +182,6 @@ function ReplyList() {
"event",
async (data) => {
const event = LumeEvent.from(data.payload.raw, data.payload.parsed);
console.log(event);
await queryClient.setQueryData(
["replies", id],
@@ -200,6 +198,16 @@ function ReplyList() {
};
}, []);
useEffect(() => {
const unlisten = getCurrentWindow().listen(id, async () => {
await queryClient.invalidateQueries({ queryKey: ["replies", id] });
});
return () => {
unlisten.then((f) => f());
};
}, []);
return (
<div className="px-3">
<div className="flex items-center text-sm font-semibold h-14 text-neutral-600 dark:text-white/30">
@@ -0,0 +1,9 @@
import { commands } from "@/commands.gen";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/columns/_layout/events/$id")({
beforeLoad: async () => {
const accounts = await commands.getAccounts();
return { accounts };
},
});
@@ -88,7 +88,7 @@ export function Screen() {
>
<ScrollArea.Viewport
ref={ref}
className="relative h-full bg-white dark:bg-black rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
className="relative h-full bg-white dark:bg-neutral-800 rounded-t-xl shadow shadow-neutral-300/50 dark:shadow-none border-[.5px] border-neutral-300 dark:border-neutral-700"
>
<Virtualizer scrollRef={ref as unknown as RefObject<HTMLElement>}>
{isFetching && !isLoading && !isFetchingNextPage ? (
+200 -49
View File
@@ -1,5 +1,5 @@
import { commands } from "@/commands.gen";
import { cn, toLumeEvents } from "@/commons";
import { cn, isValidRelayUrl, toLumeEvents } from "@/commons";
import { Spinner, User } from "@/components";
import { LumeWindow } from "@/system";
import type { LumeColumn, NostrEvent } from "@/types";
@@ -8,9 +8,10 @@ import * as ScrollArea from "@radix-ui/react-scroll-area";
import { useQuery } from "@tanstack/react-query";
import { createLazyFileRoute } from "@tanstack/react-router";
import { resolveResource } from "@tauri-apps/api/path";
import { message } from "@tauri-apps/plugin-dialog";
import { readTextFile } from "@tauri-apps/plugin-fs";
import { nanoid } from "nanoid";
import { useCallback } from "react";
import { useCallback, useState, useTransition } from "react";
export const Route = createLazyFileRoute("/columns/_layout/launchpad/$id")({
component: Screen,
@@ -25,6 +26,7 @@ function Screen() {
>
<ScrollArea.Viewport className="relative h-full px-3 pb-3">
<Newsfeeds />
<Relayfeeds />
<Interests />
<Core />
</ScrollArea.Viewport>
@@ -62,11 +64,11 @@ function Newsfeeds() {
const renderItem = useCallback(
(item: NostrEvent) => {
const users = item.tags.filter((tag) => tag[0] === "p");
const name =
item.kind === 3
? "Contacts"
: item.tags.find((tag) => tag[0] === "title")?.[1] || "Unnamed";
const label =
item.kind === 3
? `newsfeed-${id.slice(0, 5)}`
@@ -78,32 +80,24 @@ function Newsfeeds() {
className="group flex flex-col rounded-xl overflow-hidden bg-white dark:bg-neutral-800/50 shadow-lg shadow-primary dark:ring-1 dark:ring-neutral-800"
>
<div className="px-2 pt-2">
<ScrollArea.Root
type={"scroll"}
scrollHideDelay={300}
className="overflow-hidden size-full"
>
<ScrollArea.Viewport className="p-3 h-16 bg-neutral-100 dark:bg-neutral-800 rounded-lg">
<div className="flex flex-wrap items-center justify-center gap-2">
{item.tags
.filter((tag) => tag[0] === "p")
.map((tag) => (
<User.Provider key={tag[1]} pubkey={tag[1]}>
<User.Root>
<User.Avatar className="size-8 rounded-full" />
</User.Root>
</User.Provider>
))}
</div>
</ScrollArea.Viewport>
<ScrollArea.Scrollbar
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
orientation="vertical"
>
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
</ScrollArea.Scrollbar>
<ScrollArea.Corner className="bg-transparent" />
</ScrollArea.Root>
<div className="p-3 h-16 bg-neutral-100 dark:bg-neutral-800 rounded-lg">
<div className="flex flex-wrap items-center justify-center gap-2">
{users.slice(0, 5).map((tag) => (
<User.Provider key={tag[1]} pubkey={tag[1]}>
<User.Root>
<User.Avatar className="size-8 rounded-full" />
</User.Root>
</User.Provider>
))}
{users.length > 5 ? (
<div className="size-8 rounded-full inline-flex items-center justify-center bg-neutral-300 dark:bg-neutral-700">
<p className="truncate leading-tight text-[8px] font-medium">
+{users.length - 5}
</p>
</div>
) : null}
</div>
</div>
</div>
<div className="p-2 flex items-center justify-between">
<div className="inline-flex items-center gap-2">
@@ -186,14 +180,14 @@ function Newsfeeds() {
type="button"
onClick={() =>
LumeWindow.openColumn({
name: "Newsfeeds",
name: "Browse Newsfeeds",
url: "/columns/discover-newsfeeds",
label: "discover_newsfeeds",
})
}
className="h-9 w-full px-3 flex items-center justify-between bg-neutral-200/50 hover:bg-neutral-200 rounded-lg dark:bg-neutral-800/50 dark:hover:bg-neutral-800"
>
<span className="text-xs font-medium">Discover newsfeeds</span>
<span className="text-xs font-medium">Browse newsfeeds</span>
<ArrowRight className="size-4" weight="bold" />
</button>
</div>
@@ -201,6 +195,179 @@ function Newsfeeds() {
);
}
function Relayfeeds() {
const { id } = Route.useParams();
const { isLoading, isError, error, data, refetch, isRefetching } = useQuery({
queryKey: ["relays", id],
queryFn: async () => {
const res = await commands.getRelayList(id);
if (res.status === "ok") {
const event: NostrEvent = JSON.parse(res.data);
return event;
} else {
throw new Error(res.error);
}
},
refetchOnWindowFocus: false,
});
return (
<div className="mb-12 flex flex-col gap-3">
<div className="flex items-center justify-between px-2">
<h3 className="font-semibold">Relayfeeds</h3>
<div className="inline-flex items-center justify-center gap-2">
<button
type="button"
onClick={() => refetch()}
className={cn(
"size-7 inline-flex items-center justify-center rounded-full",
isRefetching ? "animate-spin" : "",
)}
>
<ArrowClockwise className="size-4" />
</button>
<button
type="button"
onClick={() => LumeWindow.openPopup(`${id}/set-group`, "New group")}
className="h-7 w-max px-2 inline-flex items-center justify-center gap-1 text-sm font-medium rounded-full bg-neutral-300 dark:bg-neutral-700 hover:bg-blue-500 hover:text-white"
>
<Plus className="size-3" weight="bold" />
New
</button>
</div>
</div>
<div className="flex flex-col gap-3">
{isLoading ? (
<div className="inline-flex items-center gap-1.5">
<Spinner className="size-4" />
Loading...
</div>
) : isError ? (
<div className="flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-center">{error?.message ?? "Error"}</p>
</div>
) : !data ? (
<div className="flex flex-col items-center justify-center h-16 w-full rounded-xl overflow-hidden bg-neutral-200/50 dark:bg-neutral-800/50">
<p className="text-center">You don't have any relay list yet.</p>
</div>
) : (
<div className="flex flex-col rounded-xl overflow-hidden bg-white dark:bg-neutral-800/50 shadow-lg shadow-primary dark:ring-1 dark:ring-neutral-800">
<div className="flex flex-col gap-2 p-2">
{data?.tags.map((tag) =>
tag[1]?.startsWith("wss://") ? (
<div
key={tag[1]}
className="group px-3 flex items-center justify-between h-11 rounded-lg bg-neutral-100 dark:bg-neutral-800"
>
<div className="flex-1 truncate select-text text-sm font-medium">
{tag[1]}
</div>
<button
type="button"
onClick={() =>
LumeWindow.openColumn({
name: tag[1],
label: `relays_${tag[1].replace(/[^\w\s]/gi, "")}`,
url: `/columns/relays/${encodeURIComponent(tag[1])}`,
})
}
className="h-6 w-16 hidden group-hover:inline-flex items-center justify-center gap-1 text-xs font-semibold rounded-full bg-neutral-200 dark:bg-neutral-700 hover:bg-blue-500 hover:text-white"
>
Add
</button>
</div>
) : null,
)}
</div>
<div className="p-2 flex items-center">
<User.Provider pubkey={data?.pubkey}>
<User.Root className="inline-flex items-center gap-2">
<User.Avatar className="size-7 rounded-full" />
<User.Name className="text-xs font-medium" />
</User.Root>
</User.Provider>
</div>
</div>
)}
<div className="flex flex-col rounded-xl overflow-hidden bg-white dark:bg-neutral-800/50 shadow-lg shadow-primary dark:ring-1 dark:ring-neutral-800">
<RelayForm />
</div>
<button
type="button"
onClick={() =>
LumeWindow.openColumn({
name: "Browse Relays",
url: "/columns/discover-relays",
label: "discover_relays",
})
}
className="h-9 w-full px-3 flex items-center justify-between bg-neutral-200/50 hover:bg-neutral-200 rounded-lg dark:bg-neutral-800/50 dark:hover:bg-neutral-800"
>
<span className="text-xs font-medium">Browse relays</span>
<ArrowRight className="size-4" weight="bold" />
</button>
</div>
</div>
);
}
function RelayForm() {
const [url, setUrl] = useState("");
const [isPending, startTransition] = useTransition();
const submit = () => {
startTransition(async () => {
if (!isValidRelayUrl(url)) {
await message("Relay URL is not valid", { kind: "error" });
return;
}
await LumeWindow.openColumn({
name: url,
label: `relays_${url.replace(/[^\w\s]/gi, "")}`,
url: `/columns/relays/${encodeURIComponent(url)}`,
});
setUrl("");
});
};
return (
<div className="flex flex-col gap-2 p-2">
<label
htmlFor="url"
className="text-xs font-semibold text-neutral-700 dark:text-neutral-300"
>
Add custom relay
</label>
<div className="flex gap-2">
<input
name="url"
type="url"
onChange={(e) => setUrl(e.currentTarget.value)}
onKeyDown={(event) => {
if (event.key === "Enter") submit();
}}
value={url}
disabled={isPending}
placeholder="wss://..."
spellCheck={false}
className="flex-1 px-3 bg-neutral-100 border-transparent rounded-lg h-9 dark:bg-neutral-900 placeholder:text-neutral-600 focus:border-blue-500 focus:ring-0 dark:placeholder:text-neutral-400"
/>
<button
type="button"
disabled={isPending}
onClick={() => submit()}
className="shrink-0 h-9 w-16 inline-flex items-center justify-center gap-1 text-xs font-semibold rounded-lg bg-neutral-200 dark:bg-neutral-700 hover:bg-blue-500 hover:text-white"
>
Add
</button>
</div>
</div>
);
}
function Interests() {
const { id } = Route.useParams();
const { isLoading, isError, error, data, refetch, isRefetching } = useQuery({
@@ -340,14 +507,14 @@ function Interests() {
type="button"
onClick={() =>
LumeWindow.openColumn({
name: "Interests",
name: "Browse Interests",
url: "/columns/discover-interests",
label: "discover_interests",
})
}
className="h-9 w-full px-3 flex items-center justify-between bg-neutral-200/50 hover:bg-neutral-200 rounded-lg dark:bg-neutral-800/50 dark:hover:bg-neutral-800"
>
<span className="text-xs font-medium">Discover interests</span>
<span className="text-xs font-medium">Browse interests</span>
<ArrowRight className="size-4" weight="bold" />
</button>
</div>
@@ -399,22 +566,6 @@ function Core() {
Add
</button>
</div>
<div className="px-3 flex items-center justify-between h-11 rounded-lg bg-neutral-100 dark:bg-neutral-800">
<div className="text-sm font-medium">Relays</div>
<button
type="button"
onClick={() =>
LumeWindow.openColumn({
name: "Relays",
label: "relays",
url: "/columns/discover-relays",
})
}
className="h-6 w-16 inline-flex items-center justify-center gap-1 text-xs font-semibold rounded-full bg-neutral-200 dark:bg-neutral-700 hover:bg-blue-500 hover:text-white"
>
Add
</button>
</div>
{data?.map((column) => (
<div
key={column.label}
+2 -3
View File
@@ -8,6 +8,7 @@ import type { Metadata } from "@/types";
import { CaretDown } from "@phosphor-icons/react";
import { createLazyFileRoute, useAwaited } from "@tanstack/react-router";
import { Menu, MenuItem } from "@tauri-apps/api/menu";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { message } from "@tauri-apps/plugin-dialog";
import {
useCallback,
@@ -170,9 +171,7 @@ function Screen() {
setText("");
setIsPublish(true);
await queryClient.invalidateQueries({
queryKey: ["replies", reply_to],
});
await getCurrentWindow().emit(reply_to, {});
} else {
setError(res.error);
}
+15 -7
View File
@@ -1,3 +1,4 @@
import { cn } from "@/commons";
import { createLazyFileRoute } from "@tanstack/react-router";
export const Route = createLazyFileRoute("/new")({
@@ -5,10 +6,17 @@ export const Route = createLazyFileRoute("/new")({
});
function Screen() {
const { platform } = Route.useRouteContext();
return (
<div
data-tauri-drag-region
className="bg-white/50 dark:bg-black/50 relative size-full flex items-center justify-center"
className={cn(
"relative size-full flex items-center justify-center",
platform === "windows"
? "bg-neutral-200 dark:bg-neutral-900"
: "bg-white/20 dark:bg-black/20",
)}
>
<div className="w-[350px] flex flex-col gap-8">
<div className="flex flex-col gap-1 text-center">
@@ -19,32 +27,32 @@ function Screen() {
<div className="flex flex-col gap-4">
<a
href="/new-account/connect"
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-800 ring-1 ring-black/5 dark:ring-white/5"
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-600 ring-1 ring-black/5 dark:ring-white/5"
>
<h3 className="mb-1 font-medium">Continue with Nostr Connect</h3>
<p className="text-xs text-neutral-500 dark:text-neutral-500">
<p className="text-xs text-neutral-500 dark:text-neutral-400">
Your account will be handled by a remote signer. Lume will not
store your account keys.
</p>
</a>
<a
href="/new-account/import"
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-800 ring-1 ring-black/5 dark:ring-white/5"
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-600 ring-1 ring-black/5 dark:ring-white/5"
>
<h3 className="mb-1 font-medium">Continue with Secret Key</h3>
<p className="text-xs text-neutral-500 dark:text-neutral-500">
<p className="text-xs text-neutral-500 dark:text-neutral-400">
Lume will store your keys in secure storage. You can provide a
password to add extra security.
</p>
</a>
<a
href="/new-account/watch"
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-800 ring-1 ring-black/5 dark:ring-white/5"
className="w-full p-4 rounded-xl hover:shadow-lg hover:ring-0 hover:bg-white dark:hover:bg-neutral-600 ring-1 ring-black/5 dark:ring-white/5"
>
<h3 className="mb-1 font-medium">
Continue with Public Key (Watch Mode)
</h3>
<p className="text-xs text-neutral-500 dark:text-neutral-500">
<p className="text-xs text-neutral-500 dark:text-neutral-400">
Use for experience without provide your private key, you can add
it later to publish new note.
</p>
+1 -4
View File
@@ -1,10 +1,7 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"index.html",
],
content: ["./src/**/*.{js,ts,jsx,tsx}", "index.html"],
theme: {
extend: {
keyframes: {
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

+4 -13
View File
@@ -2,19 +2,12 @@
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": [
"ESNext",
"ES2020",
"DOM",
"DOM.Iterable"
],
"lib": ["ESNext", "ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"baseUrl": "./",
"paths": {
"@/*": [
"./src/*"
]
"@/*": ["./src/*"]
},
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
@@ -26,11 +19,9 @@
"noUnusedLocals": true,
"noImplicitAny": false,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"src"
],
"include": ["src"],
"references": [
{
"path": "./tsconfig.node.json"
+1 -3
View File
@@ -6,7 +6,5 @@
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": [
"vite.config.ts"
]
"include": ["vite.config.ts"]
}