Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0c49c5141 | ||
|
|
2ed2cb3afd | ||
|
|
2bcda1f2ef |
+18
-18
@@ -20,38 +20,38 @@
|
|||||||
"@radix-ui/react-switch": "^1.1.1",
|
"@radix-ui/react-switch": "^1.1.1",
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
"@radix-ui/react-tooltip": "^1.1.3",
|
"@radix-ui/react-tooltip": "^1.1.3",
|
||||||
"@tanstack/query-broadcast-client-experimental": "^5.59.16",
|
"@tanstack/query-broadcast-client-experimental": "^5.59.20",
|
||||||
"@tanstack/query-persist-client-core": "^5.59.16",
|
"@tanstack/query-persist-client-core": "^5.59.20",
|
||||||
"@tanstack/react-query": "^5.59.16",
|
"@tanstack/react-query": "^5.59.20",
|
||||||
"@tanstack/react-router": "^1.77.5",
|
"@tanstack/react-router": "^1.81.0",
|
||||||
"@tauri-apps/api": "^2.0.3",
|
"@tauri-apps/api": "^2.1.0",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "^2.0.0",
|
"@tauri-apps/plugin-clipboard-manager": "^2.0.0",
|
||||||
"@tauri-apps/plugin-dialog": "^2.0.1",
|
"@tauri-apps/plugin-dialog": "^2.0.1",
|
||||||
"@tauri-apps/plugin-fs": "^2.0.1",
|
"@tauri-apps/plugin-fs": "^2.0.2",
|
||||||
"@tauri-apps/plugin-http": "^2.0.1",
|
"@tauri-apps/plugin-http": "^2.0.1",
|
||||||
"@tauri-apps/plugin-os": "^2.0.0",
|
"@tauri-apps/plugin-os": "^2.0.0",
|
||||||
"@tauri-apps/plugin-process": "^2.0.0",
|
"@tauri-apps/plugin-process": "^2.0.0",
|
||||||
"@tauri-apps/plugin-shell": "^2.0.1",
|
"@tauri-apps/plugin-shell": "^2.0.1",
|
||||||
"@tauri-apps/plugin-store": "github:tauri-apps/tauri-plugin-store#a564510",
|
"@tauri-apps/plugin-store": "github:tauri-apps/tauri-plugin-store#a564510",
|
||||||
"@tauri-apps/plugin-updater": "^2.0.0",
|
"@tauri-apps/plugin-updater": "^2.0.0",
|
||||||
"@tauri-apps/plugin-upload": "^2.0.0",
|
"@tauri-apps/plugin-upload": "^2.1.0",
|
||||||
"@tauri-apps/plugin-window-state": "^2.0.0",
|
"@tauri-apps/plugin-window-state": "^2.0.0",
|
||||||
"bitcoin-units": "^1.0.0",
|
"bitcoin-units": "^1.0.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"embla-carousel-react": "^8.3.0",
|
"embla-carousel-react": "^8.3.1",
|
||||||
"i18next": "^23.16.4",
|
"i18next": "^23.16.5",
|
||||||
"i18next-resources-to-backend": "^1.2.1",
|
"i18next-resources-to-backend": "^1.2.1",
|
||||||
"light-bolt11-decoder": "^3.2.0",
|
"light-bolt11-decoder": "^3.2.0",
|
||||||
"minidenticons": "^4.2.1",
|
"minidenticons": "^4.2.1",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.8",
|
||||||
"nostr-tools": "^2.9.4",
|
"nostr-tools": "^2.10.1",
|
||||||
"react": "19.0.0-rc-cae764ce-20241025",
|
"react": "19.0.0-rc-cae764ce-20241025",
|
||||||
"react-currency-input-field": "^3.8.0",
|
"react-currency-input-field": "^3.8.0",
|
||||||
"react-dom": "19.0.0-rc-cae764ce-20241025",
|
"react-dom": "19.0.0-rc-cae764ce-20241025",
|
||||||
"react-hook-form": "^7.53.1",
|
"react-hook-form": "^7.53.2",
|
||||||
"react-i18next": "^15.1.0",
|
"react-i18next": "^15.1.1",
|
||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"rich-textarea": "^0.26.3",
|
"rich-textarea": "^0.26.4",
|
||||||
"use-debounce": "^10.0.4",
|
"use-debounce": "^10.0.4",
|
||||||
"virtua": "^0.34.2"
|
"virtua": "^0.34.2"
|
||||||
},
|
},
|
||||||
@@ -59,9 +59,9 @@
|
|||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
"@tailwindcss/forms": "^0.5.9",
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@tanstack/router-devtools": "^1.77.5",
|
"@tanstack/router-devtools": "^1.81.0",
|
||||||
"@tanstack/router-plugin": "^1.76.4",
|
"@tanstack/router-plugin": "^1.79.0",
|
||||||
"@tauri-apps/cli": "^2.0.4",
|
"@tauri-apps/cli": "^2.1.0",
|
||||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
||||||
"@vitejs/plugin-react": "^4.3.3",
|
"@vitejs/plugin-react": "^4.3.3",
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
"tailwindcss-content-visibility": "^1.0.0",
|
"tailwindcss-content-visibility": "^1.0.0",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.10",
|
||||||
"vite-tsconfig-paths": "^5.0.1"
|
"vite-tsconfig-paths": "^5.1.1"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@types/react": "npm:types-react@rc",
|
"@types/react": "npm:types-react@rc",
|
||||||
|
|||||||
Generated
+300
-300
File diff suppressed because it is too large
Load Diff
Generated
+756
-763
File diff suppressed because it is too large
Load Diff
@@ -33,9 +33,6 @@ tauri-plugin-theme = "2.1.2"
|
|||||||
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum" }
|
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum" }
|
||||||
tauri-specta = { version = "2.0.0-rc.15", features = ["derive", "typescript"] }
|
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 = "^2.0.0-rc.20"
|
||||||
specta-typescript = "0.0.7"
|
specta-typescript = "0.0.7"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
@@ -52,6 +49,13 @@ tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
|||||||
async-trait = "0.1.83"
|
async-trait = "0.1.83"
|
||||||
webbrowser = "1.0.2"
|
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]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
|
border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
|
||||||
share-picker = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
|
share-picker = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
],
|
],
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"Capability": {
|
"Capability": {
|
||||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```",
|
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"identifier",
|
"identifier",
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```",
|
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/PermissionEntry"
|
"$ref": "#/definitions/PermissionEntry"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
],
|
],
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"Capability": {
|
"Capability": {
|
||||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```",
|
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"identifier",
|
"identifier",
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```",
|
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/PermissionEntry"
|
"$ref": "#/definitions/PermissionEntry"
|
||||||
|
|||||||
@@ -71,11 +71,11 @@ pub async fn get_meta_from_event(content: String) -> Result<Meta, ()> {
|
|||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
pub async fn get_replies(id: String, state: State<'_, Nostr>) -> Result<Vec<RichEvent>, String> {
|
pub async fn get_replies(id: String, state: State<'_, Nostr>) -> Result<Vec<RichEvent>, String> {
|
||||||
let client = &state.client;
|
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()
|
let filter = Filter::new()
|
||||||
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
|
.kind(Kind::Comment)
|
||||||
.event(event_id);
|
.custom_tag(SingleLetterTag::uppercase(Alphabet::E), [event_id]);
|
||||||
|
|
||||||
let mut events = Events::new(&[filter.clone()]);
|
let mut events = Events::new(&[filter.clone()]);
|
||||||
|
|
||||||
@@ -523,39 +523,31 @@ pub async fn reply(content: String, to: String, state: State<'_, Nostr>) -> Resu
|
|||||||
Err(e) => return Err(e.to_string()),
|
Err(e) => return Err(e.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Detect root event from reply
|
// Find root event from reply
|
||||||
let root_ids: Vec<&EventId> = reply_to
|
let root_tag = reply_to
|
||||||
.tags
|
.tags
|
||||||
.filter_standardized(TagKind::e())
|
.find(TagKind::SingleLetter(SingleLetterTag::uppercase(
|
||||||
.filter_map(|t| match t {
|
Alphabet::E,
|
||||||
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();
|
|
||||||
|
|
||||||
// Get root event if exist
|
// Get root event if exist
|
||||||
let root = match root_ids.first() {
|
let root = match root_tag {
|
||||||
Some(&id) => client
|
Some(tag) => match tag.content() {
|
||||||
.database()
|
Some(content) => {
|
||||||
.event_by_id(id)
|
let id = EventId::parse(content).map_err(|err| err.to_string())?;
|
||||||
.await
|
|
||||||
.map_err(|err| err.to_string())?,
|
client
|
||||||
|
.database()
|
||||||
|
.event_by_id(&id)
|
||||||
|
.await
|
||||||
|
.map_err(|err| err.to_string())?
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
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)
|
.add_tags(tags)
|
||||||
.pow(DEFAULT_DIFFICULTY);
|
.pow(DEFAULT_DIFFICULTY);
|
||||||
|
|
||||||
|
|||||||
@@ -105,28 +105,27 @@ pub async fn create_column(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if let Ok(event_id) = EventId::parse(&id) {
|
} else if let Ok(event_id) = EventId::parse(&id) {
|
||||||
let is_thread = payload.url().to_string().contains("events");
|
tauri::async_runtime::spawn(async move {
|
||||||
|
let state = webview.state::<Nostr>();
|
||||||
|
let client = &state.client;
|
||||||
|
|
||||||
if is_thread {
|
let subscription_id = SubscriptionId::new(webview.label());
|
||||||
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());
|
||||||
|
|
||||||
let filter = Filter::new()
|
if let Err(e) = client
|
||||||
.event(event_id)
|
.subscribe_with_id(subscription_id, vec![filter], None)
|
||||||
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
|
.await
|
||||||
.since(Timestamp::now());
|
{
|
||||||
|
println!("Subscription error: {}", e);
|
||||||
if let Err(e) = client
|
}
|
||||||
.subscribe_with_id(subscription_id, vec![filter], None)
|
});
|
||||||
.await
|
|
||||||
{
|
|
||||||
println!("Subscription error: {}", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-17
@@ -56,13 +56,6 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
|
|||||||
// Get words
|
// Get words
|
||||||
let words: Vec<_> = content.split_whitespace().collect();
|
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
|
// Get hashtags
|
||||||
let hashtags = words
|
let hashtags = words
|
||||||
.iter()
|
.iter()
|
||||||
@@ -70,6 +63,13 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
|
|||||||
.map(|&s| s.to_string().replace("#", "").to_lowercase())
|
.map(|&s| s.to_string().replace("#", "").to_lowercase())
|
||||||
.collect::<Vec<_>>();
|
.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 {
|
for mention in mentions {
|
||||||
let entity = mention.replace("nostr:", "").replace('@', "");
|
let entity = mention.replace("nostr:", "").replace('@', "");
|
||||||
|
|
||||||
@@ -92,8 +92,11 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
|
|||||||
}
|
}
|
||||||
if entity.starts_with("note") {
|
if entity.starts_with("note") {
|
||||||
if let Ok(event_id) = EventId::from_bech32(&entity) {
|
if let Ok(event_id) = EventId::from_bech32(&entity) {
|
||||||
let hex = event_id.to_hex();
|
let tag = Tag::from_standardized(TagStandard::Quote {
|
||||||
let tag = Tag::parse(&["e", &hex, "", "mention"]).unwrap();
|
event_id,
|
||||||
|
relay_url: None,
|
||||||
|
public_key: None,
|
||||||
|
});
|
||||||
tags.push(tag);
|
tags.push(tag);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
@@ -101,14 +104,12 @@ pub fn create_tags(content: &str) -> Vec<Tag> {
|
|||||||
}
|
}
|
||||||
if entity.starts_with("nevent") {
|
if entity.starts_with("nevent") {
|
||||||
if let Ok(event) = Nip19Event::from_bech32(&entity) {
|
if let Ok(event) = Nip19Event::from_bech32(&entity) {
|
||||||
let hex = event.event_id.to_hex();
|
let relay_url = event.relays.first().map(UncheckedUrl::from);
|
||||||
let relay = event.clone().relays.into_iter().next().unwrap_or("".into());
|
let tag = Tag::from_standardized(TagStandard::Quote {
|
||||||
let tag = Tag::parse(&["e", &hex, &relay, "mention"]).unwrap();
|
event_id: event.event_id,
|
||||||
|
relay_url,
|
||||||
if let Some(author) = event.author {
|
public_key: event.author,
|
||||||
let tag = Tag::public_key(author);
|
});
|
||||||
tags.push(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
tags.push(tag);
|
tags.push(tag);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -236,8 +236,8 @@ fn main() {
|
|||||||
// Config
|
// Config
|
||||||
let opts = Options::new()
|
let opts = Options::new()
|
||||||
.gossip(true)
|
.gossip(true)
|
||||||
.max_avg_latency(Duration::from_millis(500))
|
.max_avg_latency(Duration::from_secs(2))
|
||||||
.timeout(Duration::from_secs(5));
|
.timeout(Duration::from_secs(10));
|
||||||
|
|
||||||
// Setup nostr client
|
// Setup nostr client
|
||||||
let client = ClientBuilder::default()
|
let client = ClientBuilder::default()
|
||||||
@@ -532,7 +532,7 @@ fn main() {
|
|||||||
if let Err(e) = handle_clone.emit("metadata", event.as_json()) {
|
if let Err(e) = handle_clone.emit("metadata", event.as_json()) {
|
||||||
println!("Emit error: {}", e)
|
println!("Emit error: {}", e)
|
||||||
}
|
}
|
||||||
} else if event.kind == Kind::TextNote {
|
} else if event.kind == Kind::Comment {
|
||||||
let payload = RichEvent {
|
let payload = RichEvent {
|
||||||
raw: event.as_json(),
|
raw: event.as_json(),
|
||||||
parsed: if event.kind == Kind::TextNote {
|
parsed: if event.kind == Kind::TextNote {
|
||||||
@@ -544,7 +544,7 @@ fn main() {
|
|||||||
|
|
||||||
if let Err(e) = handle_clone.emit_to(
|
if let Err(e) = handle_clone.emit_to(
|
||||||
EventTarget::labeled(subscription_id.to_string()),
|
EventTarget::labeled(subscription_id.to_string()),
|
||||||
"event",
|
"comment",
|
||||||
payload,
|
payload,
|
||||||
) {
|
) {
|
||||||
println!("Emit error: {}", e)
|
println!("Emit error: {}", e)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
"productName": "Lume",
|
"productName": "Lume",
|
||||||
"version": "24.11.6",
|
"version": "24.11.7",
|
||||||
"identifier": "nu.lume.Lume",
|
"identifier": "nu.lume.Lume",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
+3
-5
@@ -1,12 +1,12 @@
|
|||||||
/* prettier-ignore-start */
|
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
|
||||||
// This file is auto-generated by TanStack Router
|
// This file was automatically generated by TanStack Router.
|
||||||
|
// You should NOT make any changes in this file as it will be overwritten.
|
||||||
|
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||||
|
|
||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
@@ -1036,8 +1036,6 @@ export const routeTree = rootRoute
|
|||||||
._addFileChildren(rootRouteChildren)
|
._addFileChildren(rootRouteChildren)
|
||||||
._addFileTypes<FileRouteTypes>()
|
._addFileTypes<FileRouteTypes>()
|
||||||
|
|
||||||
/* prettier-ignore-end */
|
|
||||||
|
|
||||||
/* ROUTE_MANIFEST_START
|
/* ROUTE_MANIFEST_START
|
||||||
{
|
{
|
||||||
"routes": {
|
"routes": {
|
||||||
|
|||||||
@@ -99,14 +99,9 @@ function ReplyList() {
|
|||||||
const res = await commands.getReplies(id);
|
const res = await commands.getReplies(id);
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const events = res.data
|
const events = res.data.map((item) =>
|
||||||
// Create Lume Events
|
LumeEvent.from(item.raw, item.parsed),
|
||||||
.map((item) => LumeEvent.from(item.raw, item.parsed))
|
);
|
||||||
// Filter quote
|
|
||||||
.filter(
|
|
||||||
(ev) =>
|
|
||||||
!ev.tags.filter((t) => t[0] === "q" || t[3] === "mention").length,
|
|
||||||
);
|
|
||||||
|
|
||||||
return events;
|
return events;
|
||||||
} else {
|
} else {
|
||||||
@@ -179,7 +174,7 @@ function ReplyList() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = getCurrentWindow().listen<EventPayload>(
|
const unlisten = getCurrentWindow().listen<EventPayload>(
|
||||||
"event",
|
"comment",
|
||||||
async (data) => {
|
async (data) => {
|
||||||
const event = LumeEvent.from(data.payload.raw, data.payload.parsed);
|
const event = LumeEvent.from(data.payload.raw, data.payload.parsed);
|
||||||
|
|
||||||
@@ -216,7 +211,7 @@ function ReplyList() {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center gap-2">
|
<div className="flex items-center justify-center gap-2">
|
||||||
<Spinner className="size-4" />
|
<Spinner className="size-4" />
|
||||||
<span className="text-sm font-medium">Getting replies...</span>
|
<span className="text-sm font-medium">Loading replies...</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
import { commands } from "@/commands.gen";
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
export const Route = createFileRoute("/columns/_layout/events/$id")({
|
export const Route = createFileRoute("/columns/_layout/events/$id")();
|
||||||
beforeLoad: async () => {
|
|
||||||
const accounts = await commands.getAccounts();
|
|
||||||
return { accounts };
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const Route = createLazyFileRoute("/new-post/")({
|
|||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
const { reply_to } = Route.useSearch();
|
const { reply_to } = Route.useSearch();
|
||||||
const { accounts, initialValue, queryClient } = Route.useRouteContext();
|
const { accounts, initialValue } = Route.useRouteContext();
|
||||||
const { deferMentionList } = Route.useLoaderData();
|
const { deferMentionList } = Route.useLoaderData();
|
||||||
const users = useAwaited({ promise: deferMentionList })[0];
|
const users = useAwaited({ promise: deferMentionList })[0];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user