Improving gossip-lib API (moving things, hiding things, documenting things)

This commit is contained in:
Mike Dilger 2023-10-05 08:18:25 +13:00
parent eba38a0583
commit b96bca7b71
62 changed files with 1443 additions and 956 deletions

View File

@ -1,8 +1,8 @@
use bech32::FromBase32;
use gossip_lib::error::{Error, ErrorKind};
use gossip_lib::globals::GLOBALS;
use gossip_lib::people::PersonList;
use gossip_lib::person_relay::PersonRelay;
use gossip_lib::PersonList;
use gossip_lib::PersonRelay;
use gossip_lib::GLOBALS;
use gossip_lib::{Error, ErrorKind};
use nostr_types::{
Event, EventAddr, EventKind, Id, NostrBech32, NostrUrl, PrivateKey, PublicKey, RelayUrl,
UncheckedUrl, Unixtime,

View File

@ -5,11 +5,12 @@
#![allow(clippy::uninlined_format_args)]
mod commands;
mod date_ago;
mod ui;
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::error::Error;
use gossip_lib::globals::GLOBALS;
use gossip_lib::Error;
use gossip_lib::GLOBALS;
use std::ops::DerefMut;
use std::{env, thread};
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
@ -98,7 +99,7 @@ async fn tokio_main() {
.unwrap();
// Run the overlord
let mut overlord = gossip_lib::overlord::Overlord::new(overlord_receiver);
let mut overlord = gossip_lib::Overlord::new(overlord_receiver);
overlord.run().await;
}

View File

@ -1,9 +1,9 @@
use super::{GossipUi, Page};
use eframe::egui;
use egui::{Context, Label, RichText, Sense, Ui};
use gossip_lib::dm_channel::DmChannelData;
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::GLOBALS;
use gossip_lib::DmChannelData;
use gossip_lib::FeedKind;
use gossip_lib::GLOBALS;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
let mut channels: Vec<DmChannelData> = match GLOBALS.storage.dm_channels() {
@ -32,7 +32,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
));
ui.label(
RichText::new(gossip_lib::date_ago::date_ago(channeldata.latest_message))
RichText::new(crate::date_ago::date_ago(channeldata.latest_message))
.italics()
.weak(),
)

View File

@ -2,8 +2,8 @@ use super::theme::FeedProperties;
use super::{GossipUi, Page};
use eframe::egui;
use egui::{Context, Frame, RichText, Ui, Vec2};
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::GLOBALS;
use gossip_lib::FeedKind;
use gossip_lib::GLOBALS;
use nostr_types::Id;
use std::sync::atomic::Ordering;

View File

@ -6,8 +6,8 @@ use eframe::{
};
use egui::{Button, Color32, Pos2, RichText, Stroke, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::GLOBALS;
use gossip_lib::FeedKind;
use gossip_lib::GLOBALS;
use nostr_types::{ContentSegment, EventAddr, Id, IdHex, NostrBech32, PublicKey, Span, Tag, Url};
use std::{
cell::{Ref, RefCell},

View File

@ -11,9 +11,9 @@ use crate::ui::widgets::CopyButton;
use crate::ui::{GossipUi, Page};
use crate::AVATAR_SIZE_F32;
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::dm_channel::DmChannel;
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::{ZapState, GLOBALS};
use gossip_lib::DmChannel;
use gossip_lib::FeedKind;
use gossip_lib::{ZapState, GLOBALS};
pub const AVATAR_SIZE_REPOST_F32: f32 = 27.0; // points, not pixels
use eframe::egui::{self, Margin};
use egui::{
@ -528,7 +528,7 @@ fn render_note_inner(
}
ui.label(
RichText::new(gossip_lib::date_ago::date_ago(note.event.created_at))
RichText::new(crate::date_ago::date_ago(note.event.created_at))
.italics()
.weak(),
)
@ -789,7 +789,7 @@ fn render_note_inner(
// To zap, the user must have a lnurl, and the event must have been
// seen on some relays
let mut zap_lnurl = None;
let mut zap_lnurl: Option<String> = None;
if let Some(ref metadata) = note.author.metadata {
if let Some(lnurl) = metadata.lnurl() {
zap_lnurl = Some(lnurl);

View File

@ -1,5 +1,5 @@
use gossip_lib::globals::GLOBALS;
use gossip_lib::people::{Person, PersonList};
use gossip_lib::GLOBALS;
use gossip_lib::{Person, PersonList};
use nostr_types::{
ContentSegment, Event, EventDelegation, EventKind, Id, MilliSatoshi, NostrBech32, PublicKey,

View File

@ -1,5 +1,5 @@
use super::notedata::NoteData;
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
use nostr_types::{Id, PublicKey};
use std::{cell::RefCell, collections::HashMap, rc::Rc};

View File

@ -5,9 +5,9 @@ use eframe::epaint::text::LayoutJob;
use egui::containers::CollapsingHeader;
use egui::{Align, Context, Key, Layout, Modifiers, RichText, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::dm_channel::DmChannel;
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::DmChannel;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
use memoize::memoize;
use nostr_types::{ContentSegment, NostrBech32, NostrUrl, ShatteredContent, Tag};
@ -244,12 +244,12 @@ fn dm_posting_area(
});
}
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::Post(
app.dm_draft_data.draft.clone(),
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::Post {
content: app.dm_draft_data.draft.clone(),
tags,
None,
Some(dm_channel.to_owned()),
));
in_reply_to: None,
dm_channel: Some(dm_channel.to_owned()),
});
app.reset_draft();
}
@ -475,12 +475,12 @@ fn real_posting_area(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
}
match app.draft_data.replying_to {
Some(replying_to_id) => {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::Post(
app.draft_data.draft.clone(),
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::Post {
content: app.draft_data.draft.clone(),
tags,
Some(replying_to_id),
None,
));
in_reply_to: Some(replying_to_id),
dm_channel: None,
});
}
None => {
if let Some(event_id) = app.draft_data.repost {
@ -488,12 +488,12 @@ fn real_posting_area(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
.to_overlord
.send(ToOverlordMessage::Repost(event_id));
} else {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::Post(
app.draft_data.draft.clone(),
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::Post {
content: app.draft_data.draft.clone(),
tags,
None,
None,
));
in_reply_to: None,
dm_channel: None,
});
}
}
}

View File

@ -1,7 +1,7 @@
use super::{GossipUi, Page};
use eframe::egui;
use egui::{Context, Ui};
use gossip_lib::feed::FeedKind;
use gossip_lib::FeedKind;
mod about;
mod stats;

View File

@ -1,7 +1,7 @@
use super::GossipUi;
use eframe::egui;
use egui::{Context, Ui};
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
use humansize::{format_size, DECIMAL};
use std::sync::atomic::Ordering;

View File

@ -34,14 +34,14 @@ use egui::{
#[cfg(feature = "video-ffmpeg")]
use egui_video::{AudioDevice, Player};
use egui_winit::egui::Response;
use gossip_lib::about::About;
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::dm_channel::DmChannel;
use gossip_lib::error::Error;
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::{ZapState, GLOBALS};
use gossip_lib::people::{Person, PersonList};
use gossip_lib::settings::Settings;
use gossip_lib::About;
use gossip_lib::DmChannel;
use gossip_lib::Error;
use gossip_lib::FeedKind;
use gossip_lib::Settings;
use gossip_lib::{Person, PersonList};
use gossip_lib::{ZapState, GLOBALS};
use nostr_types::{Id, IdHex, Metadata, MilliSatoshi, Profile, PublicKey, UncheckedUrl, Url};
use std::collections::{HashMap, HashSet};
#[cfg(feature = "video-ffmpeg")]
@ -602,7 +602,7 @@ impl GossipUi {
submenu_ids,
submenu_state: SubMenuState::new(),
settings_tab: SettingsTab::Id,
about: gossip_lib::about::about(),
about: About::new(),
icon: icon_texture_handle,
placeholder_avatar: placeholder_avatar_texture_handle,
options_symbol,

View File

@ -2,7 +2,7 @@ use super::GossipUi;
use eframe::egui;
use egui::{Context, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
use nostr_types::{Profile, PublicKey};
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {

View File

@ -3,8 +3,8 @@ use crate::AVATAR_SIZE_F32;
use eframe::egui;
use egui::{Context, Image, RichText, Sense, Ui, Vec2};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::people::Person;
use gossip_lib::Person;
use gossip_lib::GLOBALS;
use std::sync::atomic::Ordering;
pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
@ -63,7 +63,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
{
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::UpdateFollowing(false));
.send(ToOverlordMessage::UpdateFollowing { merge: false });
}
if ui
.button("↓ Merge ↓")
@ -74,7 +74,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
{
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::UpdateFollowing(true));
.send(ToOverlordMessage::UpdateFollowing { merge: true });
}
if GLOBALS.signer.is_ready() {

View File

@ -3,8 +3,8 @@ use crate::AVATAR_SIZE_F32;
use eframe::egui;
use egui::{Context, Image, RichText, Sense, Ui, Vec2};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::people::Person;
use gossip_lib::Person;
use gossip_lib::GLOBALS;
use std::sync::atomic::Ordering;
pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
@ -45,7 +45,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
{
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::UpdateMuteList(false));
.send(ToOverlordMessage::UpdateMuteList { merge: false });
}
if ui
.button("↓ Merge ↓")
@ -54,7 +54,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
{
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::UpdateMuteList(true));
.send(ToOverlordMessage::UpdateMuteList { merge: true });
}
if GLOBALS.signer.is_ready() {

View File

@ -4,8 +4,8 @@ use crate::AVATAR_SIZE_F32;
use eframe::egui;
use egui::{Context, Frame, Image, RichText, TextEdit, Ui, Vec2};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::people::Person;
use gossip_lib::Person;
use gossip_lib::GLOBALS;
use nostr_types::{PublicKey, RelayUrl};
use serde_json::Value;

View File

@ -6,8 +6,8 @@ use crate::ui::Page;
use eframe::egui;
use egui::{Context, Ui};
use egui_winit::egui::{Id, RichText};
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
use nostr_types::RelayUrl;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {

View File

@ -7,7 +7,7 @@ use crate::ui::{
GossipUi, Page, SettingsTab,
};
use egui_winit::egui::{self, vec2, Align, Context, Id, Response, RichText, Ui};
use gossip_lib::{comms::ToOverlordMessage, globals::GLOBALS};
use gossip_lib::{comms::ToOverlordMessage, GLOBALS};
use nostr_types::{PublicKey, RelayUrl};
const COVERAGE_ENTRY_HEIGHT: f32 = 2.0 * TEXT_TOP + 1.5 * TITLE_FONT_SIZE + 14.0;

View File

@ -3,8 +3,8 @@ use crate::ui::{widgets, Page};
use eframe::egui;
use egui::{Context, Ui};
use egui_winit::egui::Id;
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
let is_editing = app.relays.edit.is_some();

View File

@ -4,8 +4,8 @@ use eframe::egui;
use egui::{Context, Ui};
use egui_winit::egui::Id;
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
let is_editing = app.relays.edit.is_some();

View File

@ -4,7 +4,7 @@ use super::{GossipUi, Page};
use eframe::egui;
use egui::{Context, Ui};
use egui_winit::egui::{vec2, Id, Rect, RichText};
use gossip_lib::{comms::ToOverlordMessage, globals::GLOBALS, relay::Relay};
use gossip_lib::{comms::ToOverlordMessage, Relay, GLOBALS};
use nostr_types::RelayUrl;
mod active;

View File

@ -4,8 +4,8 @@ use eframe::{egui, Frame};
use egui::widgets::Button;
use egui::{Context, Image, Label, RichText, Sense, Ui, Vec2};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::GLOBALS;
use gossip_lib::FeedKind;
use gossip_lib::GLOBALS;
use std::sync::atomic::Ordering;
pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut Frame, ui: &mut Ui) {
@ -95,7 +95,7 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut Frame, ui:
ui.horizontal(|ui| {
ui.label(
RichText::new(gossip_lib::date_ago::date_ago(event.created_at))
RichText::new(crate::date_ago::date_ago(event.created_at))
.italics()
.weak(),
);

View File

@ -3,8 +3,8 @@ use eframe::egui;
use egui::widgets::Slider;
use egui::{Context, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::settings::Settings;
use gossip_lib::Settings;
use gossip_lib::GLOBALS;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
ui.heading("Storage Settings");

View File

@ -1,7 +1,7 @@
use crate::ui::{GossipUi, SettingsTab};
use eframe::egui;
use egui::{Align, Context, Layout, Ui};
use gossip_lib::settings::Settings;
use gossip_lib::Settings;
mod content;
mod database;

View File

@ -5,7 +5,7 @@ use eframe::egui::{
TextFormat, TextStyle, Ui,
};
use eframe::epaint::{FontFamily, FontId, Shadow};
use gossip_lib::settings::Settings;
use gossip_lib::Settings;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

View File

@ -3,7 +3,7 @@ use eframe::egui::{self, *};
use nostr_types::{PublicKeyHex, Unixtime};
use crate::ui::{components, GossipUi};
use gossip_lib::{comms::ToOverlordMessage, globals::GLOBALS, relay::Relay};
use gossip_lib::{comms::ToOverlordMessage, Relay, GLOBALS};
use super::{
list_entry::{
@ -238,7 +238,7 @@ impl RelayEntry {
let (color, tooltip) = if self.connected {
let mut text = "Connected".to_string();
if let Some(at) = self.relay.last_connected_at {
let ago = gossip_lib::date_ago::date_ago(Unixtime(at as i64));
let ago = crate::date_ago::date_ago(Unixtime(at as i64));
text = format!("Connected since {}", ago);
}
(egui::Color32::from_rgb(0x63, 0xc8, 0x56), text) // green
@ -456,7 +456,7 @@ impl RelayEntry {
let pos = pos + vec2(STATS_COL_3_X, 0.0);
let mut ago = "".to_string();
if let Some(at) = self.relay.last_general_eose_at {
ago += gossip_lib::date_ago::date_ago(Unixtime(at as i64)).as_str();
ago += crate::date_ago::date_ago(Unixtime(at as i64)).as_str();
} else {
ago += "?";
}
@ -474,7 +474,7 @@ impl RelayEntry {
let pos = pos + vec2(STATS_COL_4_X, 0.0);
let mut ago = "".to_string();
if let Some(at) = self.relay.last_connected_at {
ago += gossip_lib::date_ago::date_ago(Unixtime(at as i64)).as_str();
ago += crate::date_ago::date_ago(Unixtime(at as i64)).as_str();
} else {
ago += "?";
}

View File

@ -3,9 +3,9 @@ use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::{Context, RichText, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::GLOBALS;
use gossip_lib::people::Person;
use gossip_lib::FeedKind;
use gossip_lib::Person;
use gossip_lib::GLOBALS;
use gossip_relay_picker::Direction;
use nostr_types::{Profile, PublicKey};
@ -19,7 +19,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
if app.wizard_state.contacts_sought {
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::UpdateFollowing(false));
.send(ToOverlordMessage::UpdateFollowing { merge: false });
app.wizard_state.contacts_sought = false;
}

View File

@ -3,7 +3,7 @@ use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::{Context, RichText, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
use zeroize::Zeroize;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
@ -78,10 +78,10 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
if !ncryptsec && app.password != app.password2 {
app.wizard_state.error = Some("ERROR: Passwords do not match".to_owned());
} else {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::ImportPriv(
app.import_priv.clone(),
app.password.clone(),
));
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::ImportPriv {
privkey: app.import_priv.clone(),
password: app.password.clone(),
});
app.import_priv.zeroize();
app.import_priv = "".to_owned();
}

View File

@ -3,7 +3,7 @@ use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::{Context, RichText, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
// If already imported, advance

View File

@ -2,9 +2,9 @@ use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::widgets::{Button, Slider};
use egui::{Align, Context, Layout};
use gossip_lib::feed::FeedKind;
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::FeedKind;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
mod follow_people;
mod import_keys;

View File

@ -3,8 +3,8 @@ use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::{Color32, Context, RichText, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
use gossip_relay_picker::Direction;
use nostr_types::RelayUrl;

View File

@ -3,8 +3,8 @@ use crate::ui::{GossipUi, Page, RichText};
use eframe::egui;
use egui::{Context, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::people::Person;
use gossip_lib::Person;
use gossip_lib::GLOBALS;
use nostr_types::Metadata;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {

View File

@ -3,8 +3,8 @@ use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::{Button, Color32, Context, RichText, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
use nostr_types::RelayUrl;
use std::collections::BTreeMap;

View File

@ -2,7 +2,7 @@ use crate::ui::wizard::WizardPage;
use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::{Context, RichText, Ui};
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
if app.wizard_state.pubkey.is_some() {

View File

@ -3,7 +3,7 @@ use crate::ui::{GossipUi, Page};
use eframe::egui;
use egui::{Context, RichText, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
use zeroize::Zeroize;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {

View File

@ -1,5 +1,5 @@
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
use nostr_types::{Event, EventKind, PublicKey, RelayUrl};
use std::collections::HashSet;

View File

@ -2,7 +2,7 @@ use super::GossipUi;
use crate::ui::widgets::CopyButton;
use eframe::egui;
use egui::{Context, Ui};
use gossip_lib::globals::GLOBALS;
use gossip_lib::GLOBALS;
use tokio::task;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {

View File

@ -2,8 +2,8 @@ use super::{GossipUi, Page};
use eframe::egui;
use egui::{Align, Color32, Context, Layout, RichText, TextEdit, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::GLOBALS;
use gossip_lib::relay::Relay;
use gossip_lib::Relay;
use gossip_lib::GLOBALS;
use lazy_static::lazy_static;
use nostr_types::Metadata;
use serde_json::map::Map;

View File

@ -4,7 +4,7 @@ use eframe::egui;
use egui::style::Margin;
use egui::{Color32, Context, Frame, Stroke, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::globals::{Globals, GLOBALS};
use gossip_lib::{Globals, GLOBALS};
use nostr_types::{KeySecurity, PublicKeyHex};
use zeroize::Zeroize;
@ -250,10 +250,10 @@ fn offer_change_password(app: &mut GossipUi, ui: &mut Ui) {
} else {
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::ChangePassphrase(
app.password.clone(),
app.password2.clone(),
));
.send(ToOverlordMessage::ChangePassphrase {
old: app.password.clone(),
new: app.password2.clone(),
});
app.password.zeroize();
app.password = "".to_owned();
app.password2.zeroize();
@ -335,10 +335,10 @@ fn offer_import_priv_key(app: &mut GossipUi, ui: &mut Ui) {
.write()
.write("Passwords do not match".to_owned());
} else {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::ImportPriv(
app.import_priv.clone(),
app.password.clone(),
));
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::ImportPriv {
privkey: app.import_priv.clone(),
password: app.password.clone(),
});
}
app.password.zeroize();
app.password = "".to_owned();
@ -366,10 +366,10 @@ fn offer_import_priv_key(app: &mut GossipUi, ui: &mut Ui) {
ui.add(text_edit_line!(app, app.password).password(true));
});
if ui.button("import").clicked() {
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::ImportPriv(
app.import_priv.clone(),
app.password.clone(),
));
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::ImportPriv {
privkey: app.import_priv.clone(),
password: app.password.clone(),
});
app.import_priv = "".to_owned();
app.password.zeroize();
app.password = "".to_owned();

View File

@ -1,5 +1,6 @@
use crate::profile::Profile;
/// Information about the gossip client
#[derive(Debug)]
pub struct About {
pub name: String,
@ -12,26 +13,34 @@ pub struct About {
pub storage_path: String,
}
pub fn about() -> About {
let data_dir = Profile::current().map_or(
"Cannot find a directory to store application data.".to_owned(),
|p| format!("{}/", p.profile_dir.display()),
);
let mut version = env!("CARGO_PKG_VERSION").to_string();
if version.contains("unstable") {
let git_hash_prefix: String = env!("GIT_HASH").chars().take(8).collect();
version = format!("{}-{}", version, git_hash_prefix);
}
About {
name: env!("CARGO_PKG_NAME").to_string(),
version,
description: env!("CARGO_PKG_DESCRIPTION").to_string(),
authors: env!("CARGO_PKG_AUTHORS").to_string(),
repository: env!("CARGO_PKG_REPOSITORY").to_string(),
homepage: env!("CARGO_PKG_HOMEPAGE").to_string(),
license: env!("CARGO_PKG_LICENSE").to_string(),
storage_path: data_dir,
impl Default for About {
fn default() -> Self {
Self::new()
}
}
impl About {
pub fn new() -> About {
let data_dir = Profile::current().map_or(
"Cannot find a directory to store application data.".to_owned(),
|p| format!("{}/", p.profile_dir.display()),
);
let mut version = env!("CARGO_PKG_VERSION").to_string();
if version.contains("unstable") {
let git_hash_prefix: String = env!("GIT_HASH").chars().take(8).collect();
version = format!("{}-{}", version, git_hash_prefix);
}
About {
name: env!("CARGO_PKG_NAME").to_string(),
version,
description: env!("CARGO_PKG_DESCRIPTION").to_string(),
authors: env!("CARGO_PKG_AUTHORS").to_string(),
repository: env!("CARGO_PKG_REPOSITORY").to_string(),
homepage: env!("CARGO_PKG_HOMEPAGE").to_string(),
license: env!("CARGO_PKG_LICENSE").to_string(),
storage_path: data_dir,
}
}
}

View File

@ -5,64 +5,182 @@ use nostr_types::{
};
use std::fmt;
/// This is a message sent to the Overlord
/// This is a message sent to the Overlord. Tasks which take any amount of time,
/// especially involving relays, are handled by the Overlord in this way. There is
/// no return value, you'll have to check various GLOBALS state later on if you
/// depend on the result. Such an architecture works best with an immediate-mode
/// renderer.
#[derive(Debug, Clone)]
pub enum ToOverlordMessage {
/// Calls [add_pubkey_relay](crate::Overlord::add_pubkey_relay)
AddPubkeyRelay(PublicKey, RelayUrl),
/// Calls [add_relay](crate::Overlord::add_relay)
AddRelay(RelayUrl),
/// Calls [advertise_relay_list](crate::Overlord::advertise_relay_list)
AdvertiseRelayList,
ChangePassphrase(String, String),
/// Calls [change_passphrase](crate::Overlord::change_passphrase)
ChangePassphrase { old: String, new: String },
/// Calls [clear_following](crate::Overlord::clear_following)
ClearFollowing,
/// Calls [clear_mute_list](crate::Overlord::clear_mute_list)
ClearMuteList,
/// Calls [delegation_reset](crate::Overlord::delegation_reset)
DelegationReset,
/// Calls [delete_post](crate::Overlord::delete_post)
DeletePost(Id),
/// Calls [delete_priv](crate::Overlord::delete_priv)
DeletePriv,
/// Calls [delete_pub](crate::Overlord::delete_pub)
DeletePub,
/// Calls [drop_relay](crate::Overlord::drop_relay)
DropRelay(RelayUrl),
/// Calls [fetch_event](crate::Overlord::fetch_event)
FetchEvent(Id, Vec<RelayUrl>),
/// Calls [fetch_event_addr](crate::Overlord::fetch_event_addr)
FetchEventAddr(EventAddr),
/// Calls [follow_pubkey](crate::Overlord::follow_pubkey)
FollowPubkey(PublicKey),
/// Calls [follow_nip05](crate::Overlord::follow_nip05)
FollowNip05(String),
/// Calls [follow_nprofile](crate::Overlord::follow_nprofile)
FollowNprofile(Profile),
/// Calls [generate_private_key](crate::Overlord::generate_private_key)
GeneratePrivateKey(String),
/// Calls [hide_or_show_relay](crate::Overlord::hide_or_show_relay)
HideOrShowRelay(RelayUrl, bool),
ImportPriv(String, String),
/// Calls [import_priv](crate::Overlord::import_priv)
ImportPriv {
// nsec, hex, or ncryptsec
privkey: String,
password: String,
},
/// Calls [import_pub](crate::Overlord::import_pub)
ImportPub(String),
/// Calls [like](crate::Overlord::like)
Like(Id, PublicKey),
/// internal (minions use this channel too)
MinionIsReady,
/// internal (minions use this channel too)
MinionJobComplete(RelayUrl, u64),
/// internal (minions use this channel too)
MinionJobUpdated(RelayUrl, u64, u64),
/// Calls [pick_relays_cmd](crate::Overlord::pick_relays_cmd)
PickRelays,
Post(String, Vec<Tag>, Option<Id>, Option<DmChannel>),
/// Calls [post](crate::Overlord::post)
Post {
content: String,
tags: Vec<Tag>,
in_reply_to: Option<Id>,
dm_channel: Option<DmChannel>,
},
/// Calls [prune_cache](crate::Overlord::prune_cache)
PruneCache,
/// Calls [prune_database](crate::Overlord::prune_database)
PruneDatabase,
/// Calls [push_follow](crate::Overlord::push_follow)
PushFollow,
/// Calls [push_metadata](crate::Overlord::push_metadata)
PushMetadata(Metadata),
/// Calls [push_mute_list](crate::Overlord::push_mute_list)
PushMuteList,
ReengageMinion(RelayUrl, Vec<RelayJob>),
RefreshFollowedMetadata,
Repost(Id),
/// Calls [rank_relay](crate::Overlord::rank_relay)
RankRelay(RelayUrl, u8),
/// internal (the overlord sends messages to itself sometimes!)
ReengageMinion(RelayUrl, Vec<RelayJob>),
/// Calls [reresh_followed_metadata](crate::Overlord::refresh_followed_metadata)
RefreshFollowedMetadata,
/// Calls [repost](crate::Overlord::repost)
Repost(Id),
/// Calls [search](crate::Overlord::search)
Search(String),
/// Calls [set_active_person](crate::Overlord::set_active_person)
SetActivePerson(PublicKey),
SetThreadFeed(Id, Id, Vec<RelayUrl>, Option<PublicKey>),
/// internal
SetThreadFeed {
id: Id,
referenced_by: Id,
relays: Vec<RelayUrl>,
author: Option<PublicKey>,
},
/// internal
SetDmChannel(DmChannel),
/// Calls [subscribe_config](crate::Overlord::subscribe_config)
SubscribeConfig(RelayUrl),
/// Calls [subscribe_discover](crate::Overlord::subscribe_discover)
SubscribeDiscover(Vec<PublicKey>, Option<Vec<RelayUrl>>),
/// Calls [shutdown](crate::Overlord::shutdown)
Shutdown,
/// Calls [unlock_key](crate::Overlord::unlock_key)
UnlockKey(String),
UpdateFollowing(bool),
UpdateMuteList(bool),
/// Calls [update_following](crate::Overlord::update_following)
UpdateFollowing { merge: bool },
/// Calls [update_metadata](crate::Overlord::update_metadata)
UpdateMetadata(PublicKey),
/// Calls [update_metadata_in_bulk](crate::Overlord::update_metadata_in_bulk)
UpdateMetadataInBulk(Vec<PublicKey>),
/// Calls [update_mute_list](crate::Overlord::update_mute_list)
UpdateMuteList { merge: bool },
/// Calls [visible_notes_changed](crate::Overlord::visible_notes_changed)
VisibleNotesChanged(Vec<Id>),
/// Calls [zap_start](crate::Overlord::zap_start)
ZapStart(Id, PublicKey, UncheckedUrl),
/// Calls [zap](crate::Overlord::zap)
Zap(Id, PublicKey, MilliSatoshi, String),
}
/// Internal to gossip-lib.
/// This is a message sent to the minions
#[derive(Debug, Clone)]
pub struct ToMinionMessage {
pub(crate) struct ToMinionMessage {
/// The minion we are addressing, based on the URL they are listening to
/// as a String. "all" means all minions.
pub target: String,
@ -71,7 +189,7 @@ pub struct ToMinionMessage {
}
#[derive(Debug, Clone)]
pub struct ToMinionPayload {
pub(crate) struct ToMinionPayload {
/// A job id, so the minion and overlord can talk about the job.
pub job_id: u64,
@ -79,7 +197,7 @@ pub struct ToMinionPayload {
}
#[derive(Debug, Clone)]
pub enum ToMinionPayloadDetail {
pub(crate) enum ToMinionPayloadDetail {
FetchEvent(Id),
FetchEventAddr(EventAddr),
PostEvent(Box<Event>),
@ -176,7 +294,7 @@ pub struct RelayJob {
pub reason: RelayConnectionReason,
// Payload sent when it was started
pub payload: ToMinionPayload,
pub(crate) payload: ToMinionPayload,
// NOTE, there is other per-relay data stored elsewhere in
// overlord.minions_task_url
// GLOBALS.relay_picker

View File

@ -3,6 +3,7 @@ use crate::globals::GLOBALS;
use nostr_types::{PublicKey, Tag};
use parking_lot::RwLock;
/// A delegation tag to use when posting events on another's behalf
#[derive(Default)]
pub struct Delegation {
// Delegatee NIP-26 delegation tag, optional

View File

@ -107,6 +107,8 @@ impl DmChannel {
}
}
/// Data about a DM channel such as when the latest message occured, how many massages
/// it has, and how many are unread.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct DmChannelData {
pub dm_channel: DmChannel,

View File

@ -1,8 +1,9 @@
use crate::comms::{ToMinionMessage, ToOverlordMessage};
/// Errors that can occur in gossip-lib
#[derive(Debug)]
pub enum ErrorKind {
BroadcastSend(tokio::sync::broadcast::error::SendError<ToMinionMessage>),
BroadcastSend(String),
BroadcastReceive(tokio::sync::broadcast::error::RecvError),
Delegation(String),
Empty(String),
@ -64,7 +65,7 @@ impl std::fmt::Display for Error {
write!(f, "{line}:")?;
}
match &self.kind {
BroadcastSend(e) => write!(f, "Error broadcasting: {e}"),
BroadcastSend(s) => write!(f, "Error broadcasting: {s}"),
BroadcastReceive(e) => write!(f, "Error receiving broadcast: {e}"),
Delegation(s) => write!(f, "NIP-26 Delegation Error: {s}"),
Empty(s) => write!(f, "{s} is empty"),
@ -142,7 +143,7 @@ where
impl From<tokio::sync::broadcast::error::SendError<ToMinionMessage>> for ErrorKind {
fn from(e: tokio::sync::broadcast::error::SendError<ToMinionMessage>) -> ErrorKind {
ErrorKind::BroadcastSend(e)
ErrorKind::BroadcastSend(format!("{}", e))
}
}

View File

@ -9,6 +9,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use tokio::task;
/// Kinds of feeds, with configuration parameteers
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FeedKind {
Followed(bool), // with replies
@ -38,6 +39,7 @@ impl std::fmt::Display for FeedKind {
}
}
/// The system that computes feeds as an ordered list of event Ids.
pub struct Feed {
pub recompute_lock: AtomicBool,
@ -150,12 +152,12 @@ impl Feed {
self.unlisten();
// Listen for Thread events
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::SetThreadFeed(
let _ = GLOBALS.to_overlord.send(ToOverlordMessage::SetThreadFeed {
id,
referenced_by,
relays,
author,
));
});
}
pub fn set_feed_to_person(&self, pubkey: PublicKey) {

View File

@ -16,7 +16,7 @@ use std::sync::RwLock;
use std::time::{Duration, SystemTime};
#[derive(Copy, Clone, Debug)]
pub enum FetchState {
enum FetchState {
Queued,
QueuedStale, // Queued, only fetching because cache is stale
InFlight,
@ -24,6 +24,7 @@ pub enum FetchState {
// If it succeeds, it is removed entirely.
}
/// System that fetches HTTP resources
#[derive(Debug, Default)]
pub struct Fetcher {
cache_dir: RwLock<PathBuf>,
@ -40,13 +41,13 @@ pub struct Fetcher {
}
impl Fetcher {
pub fn new() -> Fetcher {
pub(crate) fn new() -> Fetcher {
Fetcher {
..Default::default()
}
}
pub fn start() -> Result<(), Error> {
pub(crate) fn start() -> Result<(), Error> {
// Setup the cache directory
*GLOBALS.fetcher.cache_dir.write().unwrap() = Profile::current()?.cache_dir;

View File

@ -19,6 +19,7 @@ use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize};
use tokio::sync::{broadcast, mpsc, Mutex, RwLock};
/// The state that a Zap is in (it moves through 5 states before it is complete)
#[derive(Debug, Clone)]
pub enum ZapState {
None,
@ -28,22 +29,19 @@ pub enum ZapState {
ReadyToPay(Id, String), // String is the Zap Invoice as a string, to be shown as a QR code
}
/// Only one of these is ever created, via lazy_static!, and represents
/// global state for the rust application
/// Global data shared between threads. Access via the static ref `GLOBALS`.
pub struct Globals {
/// Is this the first run?
pub first_run: AtomicBool,
/// This is a broadcast channel. All Minions should listen on it.
/// To create a receiver, just run .subscribe() on it.
pub to_minions: broadcast::Sender<ToMinionMessage>,
pub(crate) to_minions: broadcast::Sender<ToMinionMessage>,
/// This is a mpsc channel. The Overlord listens on it.
/// To create a sender, just clone() it.
pub to_overlord: mpsc::UnboundedSender<ToOverlordMessage>,
/// This is ephemeral. It is filled during lazy_static initialization,
/// and stolen away when the Overlord is created.
/// and needs to be stolen away and given to the Overlord when the Overlord
/// is created.
pub tmp_overlord_receiver: Mutex<Option<mpsc::UnboundedReceiver<ToOverlordMessage>>>,
/// All nostr people records currently loaded into memory, keyed by pubkey
@ -123,11 +121,12 @@ pub struct Globals {
pub events_processed: AtomicU32,
/// Filter
pub filter_engine: Engine,
pub filter: Option<AST>,
pub(crate) filter_engine: Engine,
pub(crate) filter: Option<AST>,
}
lazy_static! {
/// A static reference to global data shared between threads.
pub static ref GLOBALS: Globals = {
// Setup a communications channel from the Overlord to the Minions.
@ -145,7 +144,6 @@ lazy_static! {
let filter = crate::filter::load_script(&filter_engine);
Globals {
first_run: AtomicBool::new(false),
to_minions,
to_overlord,
tmp_overlord_receiver: Mutex::new(Some(tmp_overlord_receiver)),

View File

@ -4,34 +4,89 @@
// TEMPORARILY
#![allow(clippy::uninlined_format_args)]
pub mod about;
//! Gossip lib is the core of gossip. The canonical binary crate is `gossip_bin`.
//! This library has been separated so that people can attach different non-canonical
//! user interfaces on top of this core.
//!
//! Because of the history of this API, it may be a bit clunky. But we will work to
//! improve that. Please submit PRs if you want to help. This interface will change.
//! fairly rapidly for a while and then settle down.
//!
//! Further general documentation TBD.
mod about;
pub use about::About;
/// Defines messages sent to the overlord
pub mod comms;
pub mod date_ago;
pub mod delegation;
pub mod dm_channel;
pub mod error;
pub mod feed;
pub mod fetcher;
pub mod filter;
pub mod globals;
pub mod media;
mod delegation;
pub use delegation::Delegation;
mod dm_channel;
pub use dm_channel::{DmChannel, DmChannelData};
mod error;
pub use error::{Error, ErrorKind};
mod feed;
pub use feed::{Feed, FeedKind};
mod fetcher;
pub use fetcher::Fetcher;
mod filter;
mod globals;
pub use globals::{Globals, ZapState, GLOBALS};
mod media;
pub use media::Media;
/// Rendering various names of users
pub mod names;
/// nip05 handling
pub mod nip05;
pub mod overlord;
pub mod people;
pub mod person_relay;
mod overlord;
pub use overlord::Overlord;
mod people;
pub use people::{Person, PersonList};
mod person_relay;
pub use person_relay::PersonRelay;
/// Processing incoming events
pub mod process;
pub mod profile;
pub mod relationship;
pub mod relay;
pub mod relay_picker_hooks;
pub mod settings;
pub mod signer;
pub mod status;
pub mod storage;
pub mod tags;
mod profile;
mod relationship;
mod relay;
pub use relay::Relay;
mod relay_picker_hooks;
mod settings;
pub use settings::Settings;
mod signer;
pub use signer::Signer;
mod status;
mod storage;
pub use storage::types::*;
pub use storage::Storage;
mod tags;
#[macro_use]
extern crate lazy_static;
/// The USER_AGENT string for gossip that it (may) use when fetching HTTP resources and
/// when connecting to relays
pub static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));

View File

@ -11,6 +11,7 @@ use std::time::Duration;
use tokio::sync::RwLock;
use usvg::TreeParsing;
/// System that processes media fetched from the internet
pub struct Media {
// We fetch (with Fetcher), process, and temporarily hold media
// until the UI next asks for them, at which point we remove them

View File

@ -83,7 +83,7 @@ impl Minion {
}
impl Minion {
pub async fn handle(&mut self, mut messages: Vec<ToMinionPayload>) -> Result<(), Error> {
pub(crate) async fn handle(&mut self, mut messages: Vec<ToMinionPayload>) -> Result<(), Error> {
// minion will log when it connects
tracing::trace!("{}: Minion handling started", &self.url);
@ -353,7 +353,10 @@ impl Minion {
Ok(())
}
pub async fn handle_overlord_message(&mut self, message: ToMinionPayload) -> Result<(), Error> {
pub(crate) async fn handle_overlord_message(
&mut self,
message: ToMinionPayload,
) -> Result<(), Error> {
match message.detail {
ToMinionPayloadDetail::FetchEvent(id) => {
self.sought_events

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ use std::time::Duration;
use tokio::sync::RwLock;
use tokio::task;
/// Person type, aliased to the latest version
pub type Person = crate::storage::types::Person2;
/// Lists people can be added to
@ -47,6 +48,8 @@ impl From<PersonList> for u8 {
}
}
/// Handles people and remembers what needs to be done for each, such as fetching
/// metadata or avatars.
pub struct People {
// active person's relays (pull from db as needed)
active_person: RwLock<Option<PublicKey>>,
@ -91,7 +94,7 @@ impl Default for People {
}
impl People {
pub fn new() -> People {
pub(crate) fn new() -> People {
People {
active_person: RwLock::new(None),
active_persons_write_relays: RwLock::new(vec![]),
@ -108,7 +111,7 @@ impl People {
}
// Start the periodic task management
pub fn start() {
pub(crate) fn start() {
if let Some(pk) = GLOBALS.signer.public_key() {
// Load our contact list from the database in order to populate
// last_contact_list_asof and last_contact_list_size

View File

@ -1 +1,2 @@
/// PersonRelay type, aliased to the latest version
pub type PersonRelay = crate::storage::types::PersonRelay1;

View File

@ -9,8 +9,9 @@ use nostr_types::{
};
use std::sync::atomic::Ordering;
// This processes a new event, saving the results into the database
// and also populating the GLOBALS maps.
/// This is mainly used internally to gossip-lib, but you can use it to stuff events
/// into gossip from other sources. This processes a new event, saving the results into
/// the database and also populating the GLOBALS maps.
#[async_recursion]
pub async fn process_new_event(
event: &Event,

View File

@ -1 +1,2 @@
/// Relay type, aliased to the latest version
pub type Relay = crate::storage::types::Relay1;

View File

@ -8,6 +8,7 @@ use nostr_types::{
use parking_lot::RwLock;
use tokio::task;
/// The signer which holds the user's identity and signs things on their behalf.
#[derive(Default)]
pub struct Signer {
public: RwLock<Option<PublicKey>>,

View File

@ -118,6 +118,9 @@ macro_rules! def_setting {
type RawDatabase = Database<UnalignedSlice<u8>, UnalignedSlice<u8>>;
/// The LMDB storage engine.
///
/// All calls are synchronous but fast so callers can just wait on them.
pub struct Storage {
env: Env,

View File

@ -1,5 +1,6 @@
mod person1;
pub use person1::Person1;
pub(crate) use person1::Person1;
mod person2;
pub use person2::Person2;
@ -10,10 +11,10 @@ mod relay1;
pub use relay1::Relay1;
mod settings1;
pub use settings1::Settings1;
pub(crate) use settings1::Settings1;
mod settings2;
pub use settings2::Settings2;
mod theme1;
pub use theme1::{Theme1, ThemeVariant1};
pub(crate) use theme1::{Theme1, ThemeVariant1};

View File

@ -3,16 +3,37 @@ use crate::people::PersonList;
use nostr_types::{Metadata, PublicKey};
use serde::{Deserialize, Serialize};
/// A person record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Person2 {
/// Public key
pub pubkey: PublicKey,
/// Petname
pub petname: Option<String>,
/// Metadata
pub metadata: Option<Metadata>,
/// When the metadata was created
pub metadata_created_at: Option<i64>,
/// When the metadata was last received (to determine if we need to check
/// for an update)
pub metadata_last_received: i64,
/// If nip05 checked out to be valid
pub nip05_valid: bool,
/// When the nip05 was last checked (to determine if we need to check again)
pub nip05_last_checked: Option<u64>,
/// When their relay list was created (to determine if we need to check
/// for an update)
pub relay_list_created_at: Option<i64>,
/// When their relay list was last received (to determine if we need to
/// check for an update)
pub relay_list_last_received: i64,
}

View File

@ -2,35 +2,38 @@ use nostr_types::{PublicKey, RelayUrl, Unixtime};
use serde::{Deserialize, Serialize};
use speedy::{Readable, Writable};
/// A person-relay association
#[derive(Debug, Readable, Writable, Serialize, Deserialize)]
pub struct PersonRelay1 {
// The person
/// The person
pub pubkey: PublicKey,
// The relay associated with that person
/// The relay associated with that person
pub url: RelayUrl,
// The last time we fetched one of the person's events from this relay
/// The last time we fetched one of the person's events from this relay
pub last_fetched: Option<u64>,
// When we follow someone at a relay
/// When we follow someone at a relay
pub last_suggested_kind3: Option<u64>,
// When we get their nip05 and it specifies this relay
/// When we get their nip05 and it specifies this relay
pub last_suggested_nip05: Option<u64>,
// Updated when a 'p' tag on any event associates this person and relay via the
// recommended_relay_url field
/// Updated when a 'p' tag on any event associates this person and relay via the
/// recommended_relay_url field
pub last_suggested_bytag: Option<u64>,
/// If they set 'read' on their relay list
pub read: bool,
/// If they set 'write' on their relay list
pub write: bool,
// When we follow someone at a relay, this is set true
/// If the user manaully specified 'read' for this association
pub manually_paired_read: bool,
// When we follow someone at a relay, this is set true
/// If the user manaully specified 'write' for this association
pub manually_paired_write: bool,
}

View File

@ -4,17 +4,40 @@ use gossip_relay_picker::Direction;
use nostr_types::{Id, RelayInformationDocument, RelayUrl, Unixtime};
use serde::{Deserialize, Serialize};
/// A relay record
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Relay1 {
/// The url
pub url: RelayUrl,
/// How many times we successfully connected
pub success_count: u64,
/// How many times we failed to connect, plus we also count when
/// the relay drops us without us requesting that
pub failure_count: u64,
/// When we last connected to the relay
pub last_connected_at: Option<u64>,
/// When the relay last gave us an EOSE on the general feed
pub last_general_eose_at: Option<u64>,
/// What rank the user applied to this relay.
/// Valid ranks go from 0 to 9, with a default of 3. 0 means do not use.
pub rank: u64,
/// If this should be hidden in the UI
pub hidden: bool,
/// What usage this relay provides to the user
pub usage_bits: u64,
/// The NIP-11 for this relay
pub nip11: Option<RelayInformationDocument>,
/// The last time we attempted to fetch the NIP-11 for this relay
/// (in unixtime seconds)
pub last_attempt_nip11: Option<u64>,
}