wip: i'm tired
This commit is contained in:
66
src-tauri/Cargo.lock
generated
66
src-tauri/Cargo.lock
generated
@@ -2,39 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "COOP"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"border",
|
||||
"futures",
|
||||
"itertools 0.13.0",
|
||||
"keyring",
|
||||
"keyring-search",
|
||||
"nostr-connect",
|
||||
"nostr-sdk",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"specta",
|
||||
"specta-typescript",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-decorum",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-os",
|
||||
"tauri-plugin-prevent-default",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-store",
|
||||
"tauri-plugin-updater",
|
||||
"tauri-specta",
|
||||
"tokio",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
@@ -1006,6 +973,39 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "coop"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"border",
|
||||
"futures",
|
||||
"itertools 0.13.0",
|
||||
"keyring",
|
||||
"keyring-search",
|
||||
"nostr-connect",
|
||||
"nostr-sdk",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"specta",
|
||||
"specta-typescript",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-decorum",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-os",
|
||||
"tauri-plugin-prevent-default",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-store",
|
||||
"tauri-plugin-updater",
|
||||
"tauri-specta",
|
||||
"tokio",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "COOP"
|
||||
name = "coop"
|
||||
version = "0.2.0"
|
||||
description = "direct message client for desktop"
|
||||
authors = ["npub1zfss807aer0j26mwp2la0ume0jqde3823rmu97ra6sgyyg956e0s6xw445"]
|
||||
|
||||
@@ -328,6 +328,13 @@ pub async fn login(
|
||||
for url in urls.iter() {
|
||||
let _ = client.add_relay(url).await;
|
||||
let _ = client.connect_relay(url).await;
|
||||
|
||||
// Workaround for https://github.com/rust-nostr/nostr/issues/509
|
||||
// TODO: remove
|
||||
let filter = Filter::new().kind(Kind::TextNote).limit(0);
|
||||
let _ = client
|
||||
.fetch_events_from(vec![url], vec![filter], Some(Duration::from_secs(3)))
|
||||
.await;
|
||||
}
|
||||
|
||||
let mut inbox_relays = state.inbox_relays.write().await;
|
||||
@@ -341,34 +348,32 @@ pub async fn login(
|
||||
let inbox_relays = state.inbox_relays.read().await;
|
||||
let relays = inbox_relays.get(&public_key).unwrap().to_owned();
|
||||
|
||||
let sub_id = SubscriptionId::new(SUBSCRIPTION_ID);
|
||||
let subscription_id = SubscriptionId::new(SUBSCRIPTION_ID);
|
||||
|
||||
// Create a filter for getting new message
|
||||
let new_message = Filter::new()
|
||||
.kind(Kind::GiftWrap)
|
||||
.pubkey(public_key)
|
||||
.limit(0);
|
||||
|
||||
// Subscribe for new message
|
||||
if let Err(e) = client
|
||||
.subscribe_with_id_to(&relays, sub_id, vec![new_message], None)
|
||||
.subscribe_with_id_to(&relays, subscription_id, vec![new_message], None)
|
||||
.await
|
||||
{
|
||||
println!("Subscribe error: {}", e)
|
||||
};
|
||||
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::GiftWrap)
|
||||
.pubkey(public_key)
|
||||
.limit(200);
|
||||
// Create a filter for getting all gift wrapped events send to current user
|
||||
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
|
||||
|
||||
let mut rx = client
|
||||
.stream_events_from(&relays, vec![filter], Some(Duration::from_secs(40)))
|
||||
.await
|
||||
.unwrap();
|
||||
let opts = SubscribeAutoCloseOptions::default().filter(
|
||||
FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(10)),
|
||||
);
|
||||
|
||||
while let Some(event) = rx.next().await {
|
||||
println!("Event: {}", event.as_json());
|
||||
if let Ok(output) = client.subscribe_to(&relays, vec![filter], Some(opts)).await {
|
||||
println!("Output: {:?}", output)
|
||||
}
|
||||
|
||||
// handle.emit("synchronized", ()).unwrap();
|
||||
});
|
||||
|
||||
Ok(public_key.to_hex())
|
||||
|
||||
@@ -175,6 +175,17 @@ pub async fn connect_inbox_relays(
|
||||
let _ = client.add_relay(&url).await;
|
||||
let _ = client.connect_relay(&url).await;
|
||||
|
||||
// Workaround for https://github.com/rust-nostr/nostr/issues/509
|
||||
// TODO: remove
|
||||
let filter = Filter::new().kind(Kind::TextNote).limit(0);
|
||||
let _ = client
|
||||
.fetch_events_from(
|
||||
vec![url.clone()],
|
||||
vec![filter],
|
||||
Some(Duration::from_secs(3)),
|
||||
)
|
||||
.await;
|
||||
|
||||
relays.push(url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +163,7 @@ fn main() {
|
||||
.expect("Error: cannot create database.");
|
||||
|
||||
// Config
|
||||
let opts = Options::new()
|
||||
.gossip(true)
|
||||
.automatic_authentication(false)
|
||||
.max_avg_latency(Duration::from_millis(500));
|
||||
let opts = Options::new().gossip(true).max_avg_latency(Duration::from_millis(500));
|
||||
|
||||
// Setup nostr client
|
||||
let client = ClientBuilder::default()
|
||||
@@ -207,6 +204,7 @@ fn main() {
|
||||
// Connect
|
||||
client.connect().await;
|
||||
|
||||
// Return nostr client
|
||||
client
|
||||
});
|
||||
|
||||
@@ -271,30 +269,13 @@ fn main() {
|
||||
let _ = client
|
||||
.handle_notifications(|notification| async {
|
||||
#[allow(clippy::collapsible_match)]
|
||||
if let RelayPoolNotification::Message { message, relay_url, .. } = notification {
|
||||
if let RelayMessage::Auth { challenge } = message {
|
||||
match client.auth(challenge, relay_url.clone()).await {
|
||||
Ok(..) => {
|
||||
if let Ok(relay) = client.relay(relay_url).await {
|
||||
if let Err(e) = relay.resubscribe().await {
|
||||
println!("Resubscribe error: {}", e)
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-nostr/nostr/issues/509
|
||||
// TODO: remove
|
||||
let filter = Filter::new().kind(Kind::TextNote).limit(0);
|
||||
let _ = client.fetch_events(vec![filter], Some(Duration::from_secs(1))).await;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Auth error: {}", e)
|
||||
}
|
||||
}
|
||||
} else if let RelayMessage::Event { event, .. } = message {
|
||||
if let RelayPoolNotification::Message { message, .. } = notification {
|
||||
if let RelayMessage::Event { event, subscription_id, .. } = message {
|
||||
if event.kind == Kind::GiftWrap {
|
||||
if let Ok(UnwrappedGift { rumor, sender }) =
|
||||
client.unwrap_gift_wrap(&event).await
|
||||
{
|
||||
let subscription_id = subscription_id.to_string();
|
||||
let mut rumor_clone = rumor.clone();
|
||||
|
||||
// Compute event id if not exist
|
||||
@@ -312,10 +293,11 @@ fn main() {
|
||||
|
||||
// Save rumor to database to further query
|
||||
if let Err(e) = client.database().save_event(&ev).await {
|
||||
println!("[save event] error: {}", e)
|
||||
println!("Error: {}", e)
|
||||
}
|
||||
|
||||
// Emit new event to frontend
|
||||
if subscription_id == SUBSCRIPTION_ID {
|
||||
// Emit new message to current chat screen
|
||||
if let Err(e) = handle.emit(
|
||||
"event",
|
||||
EventPayload {
|
||||
@@ -323,7 +305,13 @@ fn main() {
|
||||
sender: sender.to_hex(),
|
||||
},
|
||||
) {
|
||||
println!("[emit] error: {}", e)
|
||||
println!("Emit error: {}", e)
|
||||
}
|
||||
} else {
|
||||
// Emit new message to home screen
|
||||
if let Err(e) = handle.emit("synchronized", ()) {
|
||||
println!("Emit error: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if event.kind == Kind::Metadata {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"productName": "COOP",
|
||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||
"productName": "Coop",
|
||||
"version": "0.2.0",
|
||||
"identifier": "su.reya.coop",
|
||||
"build": {
|
||||
@@ -44,9 +45,7 @@
|
||||
"targets": "all",
|
||||
"active": true,
|
||||
"category": "SocialNetworking",
|
||||
"resources": [
|
||||
"resources/*"
|
||||
],
|
||||
"resources": ["resources/*"],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
|
||||
@@ -186,7 +186,7 @@ function List() {
|
||||
>
|
||||
<ScrollArea.Viewport
|
||||
ref={scrollRef}
|
||||
className="relative h-full py-2 [&>div]:!flex [&>div]:flex-col [&>div]:justify-end [&>div]:min-h-full"
|
||||
className="relative h-full py-2 [&>div]:!flex [&>div]:flex-col [&>div]:justify-end"
|
||||
>
|
||||
<Virtualizer
|
||||
scrollRef={scrollRef as unknown as RefObject<HTMLElement>}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
X,
|
||||
} from "@phosphor-icons/react";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import * as Progress from "@radix-ui/react-progress";
|
||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link, Outlet, createLazyFileRoute } from "@tanstack/react-router";
|
||||
@@ -105,21 +104,9 @@ function ChatList() {
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
const [isSync, setIsSync] = useState(false);
|
||||
const [progress, setProgress] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(
|
||||
() => setProgress((prev) => (prev <= 100 ? prev + 4 : 100)),
|
||||
1200,
|
||||
);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unlisten = listen("synchronized", async () => {
|
||||
await queryClient.refetchQueries({ queryKey: ["chats"] });
|
||||
setIsSync(true);
|
||||
await queryClient.invalidateQueries({ queryKey: ["chats"] });
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -138,15 +125,12 @@ function ChatList() {
|
||||
const index = chats.findIndex((item) => item.pubkey === event.pubkey);
|
||||
|
||||
if (index === -1) {
|
||||
await queryClient.setQueryData(
|
||||
["chats"],
|
||||
(prevEvents: NostrEvent[]) => {
|
||||
queryClient.setQueryData(["chats"], (prevEvents: NostrEvent[]) => {
|
||||
if (!prevEvents) return prevEvents;
|
||||
if (event.pubkey === account) return;
|
||||
|
||||
return [event, ...prevEvents];
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
const newEvents = [...chats];
|
||||
|
||||
@@ -155,8 +139,9 @@ function ChatList() {
|
||||
};
|
||||
|
||||
queryClient.setQueryData(["chats"], newEvents);
|
||||
await queryClient.invalidateQueries({ queryKey: ["chats"] });
|
||||
}
|
||||
|
||||
await queryClient.invalidateQueries({ queryKey: ["chats"] });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -184,7 +169,7 @@ function ChatList() {
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : isSync && !data?.length ? (
|
||||
) : !data?.length ? (
|
||||
<div className="p-2">
|
||||
<div className="px-2 h-12 w-full rounded-lg bg-black/5 dark:bg-white/5 flex items-center justify-center text-sm">
|
||||
No chats.
|
||||
@@ -228,7 +213,6 @@ function ChatList() {
|
||||
))
|
||||
)}
|
||||
</ScrollArea.Viewport>
|
||||
{!isSync ? <SyncPopup progress={progress} /> : null}
|
||||
<ScrollArea.Scrollbar
|
||||
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
||||
orientation="vertical"
|
||||
@@ -240,29 +224,6 @@ function ChatList() {
|
||||
);
|
||||
}
|
||||
|
||||
function SyncPopup({ progress }: { progress: number }) {
|
||||
return (
|
||||
<div className="absolute bottom-0 w-full h-36 flex flex-col justify-end">
|
||||
<div className="absolute left-0 bottom-0 w-full h-32 gradient-mask-t-10 bg-white dark:bg-black" />
|
||||
<div className="relative flex flex-col items-center gap-1.5 p-4">
|
||||
<Progress.Root
|
||||
className="relative overflow-hidden bg-black/20 dark:bg-white/20 rounded-full w-full h-1"
|
||||
style={{
|
||||
transform: "translateZ(0)",
|
||||
}}
|
||||
value={progress}
|
||||
>
|
||||
<Progress.Indicator
|
||||
className="bg-blue-500 size-full transition-transform duration-[660ms] ease-[cubic-bezier(0.65, 0, 0.35, 1)]"
|
||||
style={{ transform: `translateX(-${100 - progress}%)` }}
|
||||
/>
|
||||
</Progress.Root>
|
||||
<span className="text-center text-xs">Syncing message...</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Compose() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [target, setTarget] = useState("");
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { CoopIcon } from '@/icons/coop'
|
||||
import { createLazyFileRoute } from '@tanstack/react-router'
|
||||
import { CoopIcon } from "@/icons/coop";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createLazyFileRoute('/$account/_layout/chats/new')({
|
||||
export const Route = createLazyFileRoute("/$account/_layout/chats/new")({
|
||||
component: Screen,
|
||||
})
|
||||
});
|
||||
|
||||
function Screen() {
|
||||
return (
|
||||
@@ -13,8 +13,8 @@ function Screen() {
|
||||
>
|
||||
<CoopIcon className="size-10 text-neutral-200 dark:text-neutral-800" />
|
||||
<h1 className="text-center font-bold text-neutral-300 dark:text-neutral-700">
|
||||
coop on nostr.
|
||||
let's gathering on nostr.
|
||||
</h1>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user