Skip to content

Commit

Permalink
feat: support NIP22 for comment
Browse files Browse the repository at this point in the history
  • Loading branch information
reyamir committed Nov 10, 2024
1 parent ca20bbd commit 2bcda1f
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 104 deletions.
20 changes: 10 additions & 10 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ tauri-plugin-theme = "2.1.2"
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum" }
tauri-specta = { version = "2.0.0-rc.15", features = ["derive", "typescript"] }

nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }

specta = "^2.0.0-rc.20"
specta-typescript = "0.0.7"
tokio = { version = "1", features = ["full"] }
Expand All @@ -52,6 +49,13 @@ tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
async-trait = "0.1.83"
webbrowser = "1.0.2"

nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }

[patch.'https://github.com/rust-nostr/nostr']
nostr-sdk = { git = "https://github.com/reyamir/nostr", branch = "feat/nip-22", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/reyamir/nostr", branch = "feat/nip-22" }

[target.'cfg(target_os = "macos")'.dependencies]
border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
share-picker = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
Expand Down
52 changes: 22 additions & 30 deletions src-tauri/src/commands/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ pub async fn get_meta_from_event(content: String) -> Result<Meta, ()> {
#[specta::specta]
pub async fn get_replies(id: String, state: State<'_, Nostr>) -> Result<Vec<RichEvent>, String> {
let client = &state.client;
let event_id = EventId::parse(&id).map_err(|err| err.to_string())?;

let event_id = EventId::parse(&id).map_err(|err| err.to_string())?;
let filter = Filter::new()
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
.event(event_id);
.kind(Kind::Comment)
.custom_tag(SingleLetterTag::uppercase(Alphabet::E), [event_id]);

let mut events = Events::new(&[filter.clone()]);

Expand Down Expand Up @@ -523,39 +523,31 @@ pub async fn reply(content: String, to: String, state: State<'_, Nostr>) -> Resu
Err(e) => return Err(e.to_string()),
};

// Detect root event from reply
let root_ids: Vec<&EventId> = reply_to
// Find root event from reply
let root_tag = reply_to
.tags
.filter_standardized(TagKind::e())
.filter_map(|t| match t {
TagStandard::Event {
event_id, marker, ..
} => {
if let Some(mkr) = marker {
match mkr {
Marker::Root => Some(event_id),
Marker::Reply => Some(event_id),
_ => None,
}
} else {
Some(event_id)
}
}
_ => None,
})
.collect();
.find(TagKind::SingleLetter(SingleLetterTag::uppercase(
Alphabet::E,
)));

// Get root event if exist
let root = match root_ids.first() {
Some(&id) => client
.database()
.event_by_id(id)
.await
.map_err(|err| err.to_string())?,
let root = match root_tag {
Some(tag) => match tag.content() {
Some(content) => {
let id = EventId::parse(content).map_err(|err| err.to_string())?;

client
.database()
.event_by_id(&id)
.await
.map_err(|err| err.to_string())?
}
None => None,
},
None => None,
};

let builder = EventBuilder::text_note_reply(content, &reply_to, root.as_ref(), None)
let builder = EventBuilder::comment(content, &reply_to, root.as_ref(), None)
.add_tags(tags)
.pow(DEFAULT_DIFFICULTY);

Expand Down
43 changes: 21 additions & 22 deletions src-tauri/src/commands/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,27 @@ pub async fn create_column(
});
}
} else if let Ok(event_id) = EventId::parse(&id) {
let is_thread = payload.url().to_string().contains("events");

if is_thread {
tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;

let subscription_id = SubscriptionId::new(webview.label());

let filter = Filter::new()
.event(event_id)
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
.since(Timestamp::now());

if let Err(e) = client
.subscribe_with_id(subscription_id, vec![filter], None)
.await
{
println!("Subscription error: {}", e);
}
});
}
tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;

let subscription_id = SubscriptionId::new(webview.label());

let filter = Filter::new()
.custom_tag(
SingleLetterTag::uppercase(Alphabet::E),
[event_id],
)
.kind(Kind::Comment)
.since(Timestamp::now());

if let Err(e) = client
.subscribe_with_id(subscription_id, vec![filter], None)
.await
{
println!("Subscription error: {}", e);
}
});
}
}
}
Expand Down
35 changes: 18 additions & 17 deletions src-tauri/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,20 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
// Get words
let words: Vec<_> = content.split_whitespace().collect();

// Get mentions
let mentions = words
.iter()
.filter(|&&word| ["nostr:", "@"].iter().any(|&el| word.starts_with(el)))
.map(|&s| s.to_string())
.collect::<Vec<_>>();

// Get hashtags
let hashtags = words
.iter()
.filter(|&&word| word.starts_with('#'))
.map(|&s| s.to_string().replace("#", "").to_lowercase())
.collect::<Vec<_>>();

// Get mentions
let mentions = words
.iter()
.filter(|&&word| ["nostr:", "@"].iter().any(|&el| word.starts_with(el)))
.map(|&s| s.to_string())
.collect::<Vec<_>>();

for mention in mentions {
let entity = mention.replace("nostr:", "").replace('@', "");

Expand All @@ -92,23 +92,24 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
}
if entity.starts_with("note") {
if let Ok(event_id) = EventId::from_bech32(&entity) {
let hex = event_id.to_hex();
let tag = Tag::parse(&["e", &hex, "", "mention"]).unwrap();
let tag = Tag::from_standardized(TagStandard::Quote {
event_id,
relay_url: None,
public_key: None,
});
tags.push(tag);
} else {
continue;
}
}
if entity.starts_with("nevent") {
if let Ok(event) = Nip19Event::from_bech32(&entity) {
let hex = event.event_id.to_hex();
let relay = event.clone().relays.into_iter().next().unwrap_or("".into());
let tag = Tag::parse(&["e", &hex, &relay, "mention"]).unwrap();

if let Some(author) = event.author {
let tag = Tag::public_key(author);
tags.push(tag);
}
let relay_url = event.relays.first().map(UncheckedUrl::from);
let tag = Tag::from_standardized(TagStandard::Quote {
event_id: event.event_id,
relay_url,
public_key: event.author,
});

tags.push(tag);
} else {
Expand Down
8 changes: 4 additions & 4 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ fn main() {
// Config
let opts = Options::new()
.gossip(true)
.max_avg_latency(Duration::from_millis(500))
.timeout(Duration::from_secs(5));
.max_avg_latency(Duration::from_secs(2))
.timeout(Duration::from_secs(10));

// Setup nostr client
let client = ClientBuilder::default()
Expand Down Expand Up @@ -532,7 +532,7 @@ fn main() {
if let Err(e) = handle_clone.emit("metadata", event.as_json()) {
println!("Emit error: {}", e)
}
} else if event.kind == Kind::TextNote {
} else if event.kind == Kind::Comment {
let payload = RichEvent {
raw: event.as_json(),
parsed: if event.kind == Kind::TextNote {
Expand All @@ -544,7 +544,7 @@ fn main() {

if let Err(e) = handle_clone.emit_to(
EventTarget::labeled(subscription_id.to_string()),
"event",
"comment",
payload,
) {
println!("Emit error: {}", e)
Expand Down
15 changes: 5 additions & 10 deletions src/routes/columns/_layout/events.$id.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,9 @@ function ReplyList() {
const res = await commands.getReplies(id);

if (res.status === "ok") {
const events = res.data
// Create Lume Events
.map((item) => LumeEvent.from(item.raw, item.parsed))
// Filter quote
.filter(
(ev) =>
!ev.tags.filter((t) => t[0] === "q" || t[3] === "mention").length,
);
const events = res.data.map((item) =>
LumeEvent.from(item.raw, item.parsed),
);

return events;
} else {
Expand Down Expand Up @@ -179,7 +174,7 @@ function ReplyList() {

useEffect(() => {
const unlisten = getCurrentWindow().listen<EventPayload>(
"event",
"comment",
async (data) => {
const event = LumeEvent.from(data.payload.raw, data.payload.parsed);

Expand Down Expand Up @@ -216,7 +211,7 @@ function ReplyList() {
{isLoading ? (
<div className="flex items-center justify-center gap-2">
<Spinner className="size-4" />
<span className="text-sm font-medium">Getting replies...</span>
<span className="text-sm font-medium">Loading replies...</span>
</div>
) : (
<div className="flex flex-col gap-3">
Expand Down
8 changes: 1 addition & 7 deletions src/routes/columns/_layout/events.$id.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
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 };
},
});
export const Route = createFileRoute("/columns/_layout/events/$id")();
2 changes: 1 addition & 1 deletion src/routes/new-post/index.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const Route = createLazyFileRoute("/new-post/")({

function Screen() {
const { reply_to } = Route.useSearch();
const { accounts, initialValue, queryClient } = Route.useRouteContext();
const { accounts, initialValue } = Route.useRouteContext();
const { deferMentionList } = Route.useLoaderData();
const users = useAwaited({ promise: deferMentionList })[0];

Expand Down

0 comments on commit 2bcda1f

Please sign in to comment.