fetch top level messages from db

This commit is contained in:
Jack Chakany 2025-03-20 10:06:03 -04:00
parent 4dc702bf5b
commit a68e5d5324
2 changed files with 74 additions and 20 deletions

View file

@ -2,14 +2,17 @@ use std::path::PathBuf;
use std::sync::LazyLock;
use anyhow::Result;
use egui_extras::Table;
use include_dir::{include_dir, Dir};
use nostr::{Event, Kind};
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ValueRef};
use rusqlite::Connection;
use rusqlite_migration::{Migrations, M};
use serde_json::json;
use crate::account_manager::AccountManager;
use crate::mail_event::MAIL_EVENT_KIND;
use crate::mail_event::{MailMessage, MAIL_EVENT_KIND};
use crate::TableEntry;
static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations");
@ -30,11 +33,7 @@ impl Db {
Ok(Self { connection: conn })
}
pub fn store_event(
&self,
event: &Event,
account_manager: &mut AccountManager,
) -> Result<()> {
pub fn store_event(&self, event: &Event, account_manager: &mut AccountManager) -> Result<()> {
// Try to unwrap the gift wrap if this event is a gift wrap
let store_unwrapped =
is_gift_wrap(event) && account_manager.unwrap_gift_wrap(event).is_ok();
@ -44,7 +43,10 @@ impl Db {
let mut rumor = unwrapped.rumor.clone();
rumor.ensure_id();
let id = rumor.id.expect("Invalid Gift Wrapped Event: There is no ID!").to_hex();
let id = rumor
.id
.expect("Invalid Gift Wrapped Event: There is no ID!")
.to_hex();
let raw = json!(rumor).to_string();
self.connection.execute(
@ -76,6 +78,41 @@ impl Db {
Ok(count > 0)
}
/// These messages will be displayed inside the top-level table.
pub fn get_top_level_messages(&self) -> Result<Vec<TableEntry>> {
let mut stmt = self.connection
.prepare(
"SELECT DISTINCT e.id, e.content, e.created_at, e.pubkey, jsonb_extract(value, '$[1]') AS subject
FROM events e, json_each(e.tags) AS tag
WHERE jsonb_extract(tag.value, '$[0]') = 'subject'
AND (EXISTS (
SELECT 1
FROM json_each(e.tags) AS tag
WHERE jsonb_extract(tag.value, '$[0]') = 'e'
)
OR NOT EXISTS (
SELECT 1
FROM events f, json_each(f.tags) AS tag
WHERE jsonb_extract(tag.value, '$[0]') = 'e'
AND jsonb_extract(tag.value, '$[1]') = e.id
))
ORDER BY created_at DESC
")?;
let msgs_iter = stmt.query_map([], |row| {
Ok(TableEntry {
id: row.get(0)?,
content: row.get(1)?,
created_at: row.get(2)?,
pubkey: row.get(3)?,
subject: row.get(4)?,
})
})?;
let messages = msgs_iter.collect::<Result<Vec<TableEntry>, rusqlite::Error>>()?;
Ok(messages)
}
/// Get all event IDs for mail events
pub fn get_mail_event_ids(&self) -> Result<Vec<String>> {
let mut stmt = self

View file

@ -3,10 +3,11 @@
use eframe::egui::{self, FontDefinitions, Sense, Vec2b};
use egui::FontFamily::Proportional;
use egui_extras::{Column, TableBuilder};
use nostr::{EventId, SingleLetterTag, TagKind};
use relay::RelayMessage;
use rusqlite::types::FromSql;
use std::collections::HashMap;
use std::str::FromStr;
use nostr::{EventId, SingleLetterTag, TagKind};
use tracing::{debug, error, info, Level};
mod account_manager;
@ -19,6 +20,15 @@ mod ui;
// not sure if i will use this but i'm committing it for later.
// mod threaded_event;
// WE PROBABLY SHOULDN'T MAKE EVERYTHING A STRING, GRR!
pub struct TableEntry {
pub id: String,
pub content: String,
pub subject: String,
pub pubkey: String,
pub created_at: i64,
}
fn main() -> Result<(), eframe::Error> {
let (non_blocking, _guard) = tracing_appender::non_blocking(std::io::stdout()); // add log files in prod one day
tracing_subscriber::fmt()
@ -88,6 +98,7 @@ pub struct Hoot {
events: Vec<nostr::Event>,
account_manager: account_manager::AccountManager,
db: db::Db,
table_entries: Vec<TableEntry>,
}
#[derive(Debug, PartialEq)]
@ -110,6 +121,12 @@ fn update_app(app: &mut Hoot, ctx: &egui::Context) {
Ok(..) => {}
Err(v) => error!("something went wrong trying to load keys: {}", v),
}
match app.db.get_top_level_messages() {
Ok(msgs) => app.table_entries = msgs,
Err(e) => error!("Could not fetch table entries to display from DB: {}", e),
}
let _ = app
.relays
.add_url("wss://relay.chakany.systems".to_string(), wake_up.clone());
@ -291,6 +308,12 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
Page::Inbox => {
// Top bar with search
ui.horizontal(|ui| {
if ui.button("Refresh").clicked() {
match app.db.get_top_level_messages() {
Ok(msgs) => app.table_entries = msgs,
Err(e) => error!("Could not fetch table entries to display from DB: {}", e),
}
}
ui.add_space(16.0);
let search_width = ui.available_width() - 100.0;
ui.add_sized(
@ -332,7 +355,7 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
})
.body(|mut body| {
let row_height = 30.0;
let events = app.events.clone();
let events = &app.table_entries;
body.rows(row_height, events.len(), |mut row| {
let event = &events[row.index()];
@ -343,25 +366,18 @@ fn render_app(app: &mut Hoot, ctx: &egui::Context) {
ui.checkbox(&mut false, "");
});
row.col(|ui| {
ui.label(event.pubkey.to_string());
ui.label(&event.pubkey);
});
row.col(|ui| {
// Try to get subject from tags
let subject = match &event.tags.find(nostr::TagKind::Subject) {
Some(s) => match s.content() {
Some(c) => format!("{}: {}", c.to_string(), event.content),
None => event.content.clone(),
},
None => event.content.clone(),
};
ui.label(subject);
ui.label(&event.subject);
});
row.col(|ui| {
ui.label("2 minutes ago");
ui.label(event.created_at.to_string());
});
if row.response().clicked() {
app.focused_post = event.id.to_string();
app.focused_post = event.id.clone();
app.page = Page::Post;
}
});
@ -500,6 +516,7 @@ impl Hoot {
events: Vec::new(),
account_manager: account_manager::AccountManager::new(),
db,
table_entries: Vec::new(),
}
}
}