This commit is contained in:
kieran 2025-01-07 14:13:41 +00:00
parent 0e19c1a8f3
commit f7021094bc
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
16 changed files with 155 additions and 100 deletions

9
Cargo.lock generated
View File

@ -1639,7 +1639,7 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
[[package]] [[package]]
name = "enostr" name = "enostr"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.v0l.io/nostr/notedeck.git?rev=e08e30f9125b9cf7391e97a2683ba0034bff1644#e08e30f9125b9cf7391e97a2683ba0034bff1644" source = "git+https://github.com/damus-io/notedeck?rev=06417ff69e772f24ffd7fb2b025f879463d8c51f#06417ff69e772f24ffd7fb2b025f879463d8c51f"
dependencies = [ dependencies = [
"bech32", "bech32",
"ewebsock", "ewebsock",
@ -3023,7 +3023,7 @@ dependencies = [
[[package]] [[package]]
name = "notedeck" name = "notedeck"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.v0l.io/nostr/notedeck.git?rev=e08e30f9125b9cf7391e97a2683ba0034bff1644#e08e30f9125b9cf7391e97a2683ba0034bff1644" source = "git+https://github.com/damus-io/notedeck?rev=06417ff69e772f24ffd7fb2b025f879463d8c51f#06417ff69e772f24ffd7fb2b025f879463d8c51f"
dependencies = [ dependencies = [
"base32", "base32",
"dirs", "dirs",
@ -3036,6 +3036,7 @@ dependencies = [
"security-framework", "security-framework",
"serde", "serde",
"serde_json", "serde_json",
"sha2",
"strum", "strum",
"strum_macros", "strum_macros",
"thiserror 2.0.9", "thiserror 2.0.9",
@ -3047,7 +3048,7 @@ dependencies = [
[[package]] [[package]]
name = "notedeck_chrome" name = "notedeck_chrome"
version = "0.2.0" version = "0.2.0"
source = "git+https://git.v0l.io/nostr/notedeck.git?rev=e08e30f9125b9cf7391e97a2683ba0034bff1644#e08e30f9125b9cf7391e97a2683ba0034bff1644" source = "git+https://github.com/damus-io/notedeck?rev=06417ff69e772f24ffd7fb2b025f879463d8c51f#06417ff69e772f24ffd7fb2b025f879463d8c51f"
dependencies = [ dependencies = [
"android-activity 0.4.3", "android-activity 0.4.3",
"eframe", "eframe",
@ -3071,7 +3072,7 @@ dependencies = [
[[package]] [[package]]
name = "notedeck_columns" name = "notedeck_columns"
version = "0.2.0" version = "0.2.0"
source = "git+https://git.v0l.io/nostr/notedeck.git?rev=e08e30f9125b9cf7391e97a2683ba0034bff1644#e08e30f9125b9cf7391e97a2683ba0034bff1644" source = "git+https://github.com/damus-io/notedeck?rev=06417ff69e772f24ffd7fb2b025f879463d8c51f#06417ff69e772f24ffd7fb2b025f879463d8c51f"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"dirs", "dirs",

View File

@ -26,9 +26,9 @@ egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "d2ea3b4db21
# notedeck stuff # notedeck stuff
nostr = { version = "0.37.0", default-features = false, features = ["std", "nip49"] } nostr = { version = "0.37.0", default-features = false, features = ["std", "nip49"] }
nostrdb = { git = "https://github.com/damus-io/nostrdb-rs", rev = "2111948b078b24a1659d0bd5d8570f370269c99b" } nostrdb = { git = "https://github.com/damus-io/nostrdb-rs", rev = "2111948b078b24a1659d0bd5d8570f370269c99b" }
notedeck-chrome = { git = "https://git.v0l.io/nostr/notedeck.git", rev = "e08e30f9125b9cf7391e97a2683ba0034bff1644", package = "notedeck_chrome", optional = true } notedeck-chrome = { git = "https://github.com/damus-io/notedeck", rev = "06417ff69e772f24ffd7fb2b025f879463d8c51f", package = "notedeck_chrome", optional = true }
notedeck = { git = "https://git.v0l.io/nostr/notedeck.git", rev = "e08e30f9125b9cf7391e97a2683ba0034bff1644", package = "notedeck", optional = true } notedeck = { git = "https://github.com/damus-io/notedeck", rev = "06417ff69e772f24ffd7fb2b025f879463d8c51f", package = "notedeck", optional = true }
enostr = { git = "https://git.v0l.io/nostr/notedeck.git", rev = "e08e30f9125b9cf7391e97a2683ba0034bff1644", package = "enostr", optional = true } enostr = { git = "https://github.com/damus-io/notedeck", rev = "06417ff69e772f24ffd7fb2b025f879463d8c51f", package = "enostr", optional = true }
poll-promise = "0.3.0" poll-promise = "0.3.0"
ehttp = "0.5.0" ehttp = "0.5.0"

View File

@ -1,14 +1,13 @@
use crate::route::{page, RouteServices, RouteType}; use crate::profiles::ProfileLoader;
use crate::route::{page, RouteAction, RouteServices, RouteType};
use crate::widgets::{Header, NostrWidget}; use crate::widgets::{Header, NostrWidget};
use eframe::epaint::{FontFamily, Margin}; use eframe::epaint::{FontFamily, Margin};
use eframe::CreationContext; use eframe::CreationContext;
use egui::{Color32, FontData, FontDefinitions, Ui}; use egui::{Color32, FontData, FontDefinitions, Theme, Ui, Visuals};
use enostr::ewebsock::{WsEvent, WsMessage};
use enostr::{PoolEvent, RelayEvent, RelayMessage}; use enostr::{PoolEvent, RelayEvent, RelayMessage};
use log::{error, info, warn}; use log::{error, info, warn};
use nostrdb::Transaction; use nostrdb::{Filter, Transaction};
use notedeck::AppContext; use notedeck::AppContext;
use std::ops::Div;
use std::sync::mpsc; use std::sync::mpsc;
pub struct ZapStreamApp { pub struct ZapStreamApp {
@ -17,6 +16,7 @@ pub struct ZapStreamApp {
routes_tx: mpsc::Sender<RouteType>, routes_tx: mpsc::Sender<RouteType>,
widget: Box<dyn NostrWidget>, widget: Box<dyn NostrWidget>,
profiles: ProfileLoader,
} }
impl ZapStreamApp { impl ZapStreamApp {
@ -34,6 +34,7 @@ impl ZapStreamApp {
Self { Self {
current: RouteType::HomePage, current: RouteType::HomePage,
widget: Box::new(page::HomePage::new()), widget: Box::new(page::HomePage::new()),
profiles: ProfileLoader::new(),
routes_tx: tx, routes_tx: tx,
routes_rx: rx, routes_rx: rx,
} }
@ -59,16 +60,26 @@ impl notedeck::App for ZapStreamApp {
} }
} }
let mut app_frame = egui::containers::Frame::default(); // reset theme
let margin = self.frame_margin(); ui.ctx().set_visuals_of(
Theme::Dark,
Visuals {
panel_fill: Color32::BLACK,
override_text_color: Some(Color32::WHITE),
..Default::default()
},
);
app_frame.inner_margin = margin; let mut app_frame = egui::containers::Frame::default();
app_frame.stroke.color = Color32::BLACK; app_frame.inner_margin = self.frame_margin();
// handle app state changes // handle app state changes
while let Ok(r) = self.routes_rx.try_recv() { while let Ok(r) = self.routes_rx.try_recv() {
if let RouteType::Action(a) = r { if let RouteType::Action(a) = r {
match a { match a {
RouteAction::DemandProfile(p) => {
self.profiles.demand(p);
}
_ => info!("Not implemented"), _ => info!("Not implemented"),
} }
} else { } else {
@ -91,17 +102,16 @@ impl notedeck::App for ZapStreamApp {
egui::CentralPanel::default() egui::CentralPanel::default()
.frame(app_frame) .frame(app_frame)
.show(ui.ctx(), |ui| { .show(ui.ctx(), |ui| {
ui.visuals_mut().override_text_color = Some(Color32::WHITE); let tx = Transaction::new(ctx.ndb).expect("transaction");
// display app // display app
ui.vertical(|ui| { ui.vertical(|ui| {
let mut svc = RouteServices { let mut svc = RouteServices {
router: self.routes_tx.clone(), router: self.routes_tx.clone(),
tx: Transaction::new(ctx.ndb).expect("transaction"), tx: &tx,
egui: ui.ctx().clone(), egui: ui.ctx().clone(),
ctx, ctx,
}; };
Header::new().render(ui, &mut svc); Header::new().render(ui, &mut svc, &tx);
if let Err(e) = self.widget.update(&mut svc) { if let Err(e) = self.widget.update(&mut svc) {
error!("{}", e); error!("{}", e);
} }
@ -109,6 +119,15 @@ impl notedeck::App for ZapStreamApp {
}) })
.response .response
}); });
let profiles = self.profiles.next();
if !profiles.is_empty() {
info!("Profiles: {:?}", profiles);
ctx.pool.subscribe(
"profiles".to_string(),
vec![Filter::new().kinds([0]).authors(&profiles).build()],
);
}
} }
} }

View File

@ -2,14 +2,15 @@
mod android; mod android;
pub mod app; pub mod app;
mod link; mod link;
mod note_ref;
mod note_util; mod note_util;
mod note_view; mod note_view;
mod profiles;
mod route; mod route;
mod services; mod services;
mod stream_info; mod stream_info;
mod theme; mod theme;
mod widgets; mod widgets;
mod note_ref;
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
use android_activity::AndroidApp; use android_activity::AndroidApp;

30
src/profiles.rs Normal file
View File

@ -0,0 +1,30 @@
use std::collections::HashSet;
pub struct ProfileLoader {
queue: HashSet<[u8; 32]>,
fetched: HashSet<[u8; 32]>,
}
impl ProfileLoader {
pub fn new() -> Self {
Self {
queue: HashSet::new(),
fetched: HashSet::new(),
}
}
pub fn demand(&mut self, pubkey: [u8; 32]) {
if self.fetched.contains(&pubkey) {
return;
}
self.queue.insert(pubkey);
}
pub fn next(&mut self) -> Vec<[u8; 32]> {
let ret: Vec<[u8; 32]> = self.queue.drain().collect();
for p in ret.iter() {
self.fetched.insert(*p);
}
ret
}
}

View File

@ -37,7 +37,8 @@ impl NostrWidget for HomePage {
.collect(); .collect();
let events_live = NotesView::from_vec( let events_live = NotesView::from_vec(
events.iter() events
.iter()
.filter(|r| matches!(r.status(), StreamStatus::Live)) .filter(|r| matches!(r.status(), StreamStatus::Live))
.collect(), .collect(),
); );
@ -50,7 +51,8 @@ impl NostrWidget for HomePage {
.render(ui, services); .render(ui, services);
} }
let events_planned = NotesView::from_vec( let events_planned = NotesView::from_vec(
events.iter() events
.iter()
.filter(|r| matches!(r.status(), StreamStatus::Planned)) .filter(|r| matches!(r.status(), StreamStatus::Planned))
.collect(), .collect(),
); );
@ -63,7 +65,8 @@ impl NostrWidget for HomePage {
.render(ui, services); .render(ui, services);
} }
let events_ended = NotesView::from_vec( let events_ended = NotesView::from_vec(
events.iter() events
.iter()
.filter(|r| matches!(r.status(), StreamStatus::Ended)) .filter(|r| matches!(r.status(), StreamStatus::Ended))
.collect(), .collect(),
); );

View File

@ -1,21 +1,18 @@
use crate::link::NostrLink; use crate::link::NostrLink;
use crate::route::home::HomePage;
use crate::route::login::LoginPage;
use crate::route::stream::StreamPage;
use crate::services::ffmpeg_loader::FfmpegLoader; use crate::services::ffmpeg_loader::FfmpegLoader;
use crate::widgets::{Header, NostrWidget, PlaceholderRect}; use egui::load::SizedTexture;
use anyhow::{bail, Result}; use egui::{Context, Image, TextureHandle};
use egui::{Context, Image, Response, TextureHandle, Ui}; use egui_inbox::RequestRepaintTrait;
use egui_inbox::{RequestRepaintTrait, UiInbox, UiInboxSender}; use enostr::EventClientMessage;
use enostr::{EventClientMessage, Note};
use itertools::Itertools; use itertools::Itertools;
use log::{info, warn}; use log::{info, warn};
use nostr::{ClientMessage, Event, EventBuilder, JsonUtil, Kind, Tag}; use nostr::{Event, EventBuilder, JsonUtil, Kind, Tag};
use nostrdb::{Ndb, NdbProfile, NoteKey, Transaction}; use nostrdb::{NdbProfile, NoteKey, Transaction};
use notedeck::{AppContext, ImageCache}; use notedeck::{AppContext, ImageCache};
use poll_promise::Promise; use poll_promise::Promise;
use std::path::{Path, PathBuf}; use std::path::Path;
use std::sync::mpsc; use std::sync::mpsc;
use std::task::Poll;
mod home; mod home;
mod login; mod login;
@ -46,12 +43,14 @@ pub enum RouteType {
} }
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum RouteAction {} pub enum RouteAction {
DemandProfile([u8; 32]),
}
pub struct RouteServices<'a, 'ctx> { pub struct RouteServices<'a, 'ctx> {
pub router: mpsc::Sender<RouteType>, pub router: mpsc::Sender<RouteType>,
pub tx: Transaction,
pub egui: Context, pub egui: Context,
pub tx: &'a Transaction,
pub ctx: &'a mut AppContext<'ctx>, pub ctx: &'a mut AppContext<'ctx>,
} }
@ -82,8 +81,17 @@ impl<'a, 'ctx> RouteServices<'a, 'ctx> {
/// Load/Fetch profiles /// Load/Fetch profiles
pub fn profile(&self, pk: &[u8; 32]) -> Option<NdbProfile<'a>> { pub fn profile(&self, pk: &[u8; 32]) -> Option<NdbProfile<'a>> {
// TODO let p = self
None .ctx
.ndb
.get_profile_by_pubkey(self.tx, pk)
.map(|p| p.record().profile())
.ok()
.flatten();
if p.is_none() {
self.action(RouteAction::DemandProfile(pk.clone()));
}
p
} }
/// Load image from URL /// Load image from URL
@ -116,14 +124,18 @@ impl<'a, 'ctx> RouteServices<'a, 'ctx> {
None None
} }
} }
const BLACK_PIXEL: [u8; 4] = [0, 0, 0, 0];
pub fn image_from_cache<'a>(img_cache: &mut ImageCache, ctx: &Context, url: &str) -> Image<'a> { pub fn image_from_cache<'a>(img_cache: &mut ImageCache, ctx: &Context, url: &str) -> Image<'a> {
let m_cached_promise = img_cache.map().get(url); if let Some(promise) = img_cache.map().get(url) {
if m_cached_promise.is_none() { match promise.poll() {
Poll::Ready(Ok(t)) => Image::new(SizedTexture::from_handle(t)),
_ => Image::from_bytes(url.to_string(), &BLACK_PIXEL),
}
} else {
let fetch = fetch_img(img_cache, ctx, url); let fetch = fetch_img(img_cache, ctx, url);
img_cache.map_mut().insert(url.to_string(), fetch); img_cache.map_mut().insert(url.to_string(), fetch);
Image::from_bytes(url.to_string(), &BLACK_PIXEL)
} }
Image::new(url.to_string())
} }
fn fetch_img( fn fetch_img(
@ -137,7 +149,8 @@ fn fetch_img(
let ctx = ctx.clone(); let ctx = ctx.clone();
let url = url.to_owned(); let url = url.to_owned();
let dst_path = dst_path.clone(); let dst_path = dst_path.clone();
Promise::spawn_async(async move { Promise::spawn_blocking(move || {
info!("Loading image from disk: {}", dst_path.display());
match FfmpegLoader::new().load_image(dst_path) { match FfmpegLoader::new().load_image(dst_path) {
Ok(img) => Ok(ctx.load_texture(&url, img, Default::default())), Ok(img) => Ok(ctx.load_texture(&url, img, Default::default())),
Err(e) => Err(notedeck::Error::Generic(e.to_string())), Err(e) => Err(notedeck::Error::Generic(e.to_string())),
@ -159,12 +172,18 @@ fn fetch_img_from_net(
let cloned_url = url.to_owned(); let cloned_url = url.to_owned();
let cache_path = cache_path.to_owned(); let cache_path = cache_path.to_owned();
ehttp::fetch(request, move |response| { ehttp::fetch(request, move |response| {
let handle = response.map_err(notedeck::Error::Generic).map(|img| { let handle = response
std::fs::write(&cache_path, &img.bytes).unwrap(); .and_then(|img| {
let img_loaded = FfmpegLoader::new().load_image(cache_path).unwrap(); std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap();
std::fs::write(&cache_path, &img.bytes).unwrap();
info!("Loading image from net: {}", cloned_url);
let img_loaded = FfmpegLoader::new()
.load_image(cache_path)
.map_err(|e| e.to_string())?;
ctx.load_texture(&cloned_url, img_loaded, Default::default()) Ok(ctx.load_texture(&cloned_url, img_loaded, Default::default()))
}); })
.map_err(notedeck::Error::Generic);
sender.send(handle); sender.send(handle);
ctx.request_repaint(); ctx.request_repaint();

View File

@ -6,7 +6,7 @@ use crate::widgets::{
sub_or_poll, Chat, NostrWidget, PlaceholderRect, StreamPlayer, StreamTitle, WriteChat, sub_or_poll, Chat, NostrWidget, PlaceholderRect, StreamPlayer, StreamTitle, WriteChat,
}; };
use egui::{vec2, Align, Frame, Layout, Response, Stroke, Ui, Vec2, Widget}; use egui::{vec2, Align, Frame, Layout, Response, Stroke, Ui, Vec2, Widget};
use nostrdb::{Filter, Note, NoteKey, Subscription}; use nostrdb::{Filter, Note, Subscription};
use crate::note_ref::NoteRef; use crate::note_ref::NoteRef;
use std::borrow::Borrow; use std::borrow::Borrow;
@ -14,7 +14,6 @@ use std::collections::HashSet;
pub struct StreamPage { pub struct StreamPage {
link: NostrLink, link: NostrLink,
event: Option<NoteKey>,
player: Option<StreamPlayer>, player: Option<StreamPlayer>,
chat: Option<Chat>, chat: Option<Chat>,
new_msg: WriteChat, new_msg: WriteChat,
@ -28,7 +27,6 @@ impl StreamPage {
Self { Self {
new_msg: WriteChat::new(link.clone()), new_msg: WriteChat::new(link.clone()),
link, link,
event: None,
chat: None, chat: None,
player: None, player: None,
events: HashSet::new(), events: HashSet::new(),
@ -146,7 +144,11 @@ impl StreamPage {
impl NostrWidget for StreamPage { impl NostrWidget for StreamPage {
fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response { fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
let events: Vec<Note> = vec![]; let events: Vec<Note> = self
.events
.iter()
.map_while(|e| services.ctx.ndb.get_note_by_key(services.tx, e.key).ok())
.collect();
if let Some(event) = events.first() { if let Some(event) = events.first() {
if let Some(stream) = event.stream() { if let Some(stream) = event.stream() {
@ -173,14 +175,14 @@ impl NostrWidget for StreamPage {
} }
fn update(&mut self, services: &mut RouteServices<'_, '_>) -> anyhow::Result<()> { fn update(&mut self, services: &mut RouteServices<'_, '_>) -> anyhow::Result<()> {
let filt = self.get_filters(); let filters = self.get_filters();
sub_or_poll( sub_or_poll(
services.ctx.ndb, services.ctx.ndb,
&services.tx, &services.tx,
&mut services.ctx.pool, &mut services.ctx.pool,
&mut self.events, &mut self.events,
&mut self.sub, &mut self.sub,
filt, filters,
)?; )?;
if let Some(c) = self.chat.as_mut() { if let Some(c) = self.chat.as_mut() {
c.update(services)?; c.update(services)?;

View File

@ -17,11 +17,7 @@ impl FfmpegLoader {
Self::load_image_from_demuxer(demux) Self::load_image_from_demuxer(demux)
} }
pub fn load_image_bytes( pub fn load_image_bytes(&self, key: &str, data: &'static [u8]) -> Result<ColorImage, Error> {
&self,
key: &str,
data: &'static [u8],
) -> Result<ColorImage, Error> {
let demux = Demuxer::new_custom_io(data, Some(key.to_string()))?; let demux = Demuxer::new_custom_io(data, Some(key.to_string()))?;
Self::load_image_from_demuxer(demux) Self::load_image_from_demuxer(demux)
} }
@ -57,6 +53,7 @@ impl FfmpegLoader {
av_frame_free(&mut frame); av_frame_free(&mut frame);
let image = video_frame_to_image(frame_rgb); let image = video_frame_to_image(frame_rgb);
av_packet_free(&mut pkt);
return Ok(image); return Ok(image);
} }
} }

View File

@ -38,7 +38,7 @@ impl NostrWidget for Chat {
let stream = services let stream = services
.ctx .ctx
.ndb .ndb
.get_note_by_key(&services.tx, self.stream) .get_note_by_key(services.tx, self.stream)
.unwrap(); .unwrap();
ScrollArea::vertical() ScrollArea::vertical()
@ -55,9 +55,10 @@ impl NostrWidget for Chat {
.sorted_by(|a, b| a.created_at.cmp(&b.created_at)) .sorted_by(|a, b| a.created_at.cmp(&b.created_at))
{ {
if let Ok(ev) = if let Ok(ev) =
services.ctx.ndb.get_note_by_key(&services.tx, ev.key) services.ctx.ndb.get_note_by_key(services.tx, ev.key)
{ {
ChatMessage::new(&stream, &ev, &None) let profile = services.profile(ev.pubkey());
ChatMessage::new(&stream, &ev, &profile)
.render(ui, services.ctx.img_cache); .render(ui, services.ctx.img_cache);
} }
} }
@ -72,7 +73,7 @@ impl NostrWidget for Chat {
let filters = vec![self.get_filter()]; let filters = vec![self.get_filter()];
sub_or_poll( sub_or_poll(
services.ctx.ndb, services.ctx.ndb,
&services.tx, services.tx,
&mut services.ctx.pool, &mut services.ctx.pool,
&mut self.events, &mut self.events,
&mut self.sub, &mut self.sub,

View File

@ -33,7 +33,9 @@ impl<'a> ChatMessage<'a> {
job.wrap.break_anywhere = true; job.wrap.break_anywhere = true;
let is_host = self.stream.host().eq(self.ev.pubkey()); let is_host = self.stream.host().eq(self.ev.pubkey());
let name = self.profile.map_or("Nostrich", |f| f.name().map_or("Nostrich", |f| f)); let name = self
.profile
.map_or("Nostrich", |f| f.name().map_or("Nostrich", |f| f));
let name_color = if is_host { PRIMARY } else { NEUTRAL_500 }; let name_color = if is_host { PRIMARY } else { NEUTRAL_500 };

View File

@ -4,6 +4,7 @@ use crate::widgets::{Button, NostrWidget};
use eframe::emath::Align; use eframe::emath::Align;
use eframe::epaint::Vec2; use eframe::epaint::Vec2;
use egui::{CursorIcon, Frame, Layout, Margin, Response, Sense, Ui, Widget}; use egui::{CursorIcon, Frame, Layout, Margin, Response, Sense, Ui, Widget};
use nostrdb::Transaction;
pub struct Header; pub struct Header;
@ -11,10 +12,12 @@ impl Header {
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {}
} }
} pub fn render(
&mut self,
impl NostrWidget for Header { ui: &mut Ui,
fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response { services: &mut RouteServices<'_, '_>,
tx: &Transaction,
) -> Response {
let logo_bytes = include_bytes!("../resources/logo.svg"); let logo_bytes = include_bytes!("../resources/logo.svg");
Frame::none() Frame::none()
.outer_margin(Margin::symmetric(16., 8.)) .outer_margin(Margin::symmetric(16., 8.))
@ -37,7 +40,8 @@ impl NostrWidget for Header {
ui.with_layout(Layout::right_to_left(Align::Center), |ui| { ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
if let Some(acc) = services.ctx.accounts.get_selected_account() { if let Some(acc) = services.ctx.accounts.get_selected_account() {
Avatar::pubkey(&acc.pubkey, services.ctx.ndb, &services.tx).render(ui, services.ctx.img_cache); Avatar::pubkey(&acc.pubkey, services.ctx.ndb, tx)
.render(ui, services.ctx.img_cache);
} else if Button::new().show(ui, |ui| ui.label("Login")).clicked() { } else if Button::new().show(ui, |ui| ui.label("Login")).clicked() {
services.navigate(RouteType::LoginPage); services.navigate(RouteType::LoginPage);
} }
@ -47,8 +51,4 @@ impl NostrWidget for Header {
}) })
.response .response
} }
fn update(&mut self, _services: &mut RouteServices<'_, '_>) -> anyhow::Result<()> {
Ok(())
}
} }

View File

@ -66,4 +66,4 @@ impl<'a> StreamList<'a> {
}) })
.response .response
} }
} }

View File

@ -21,9 +21,8 @@ impl<'a> StreamEvent<'a> {
pub fn new(event: &'a Note<'a>) -> Self { pub fn new(event: &'a Note<'a>) -> Self {
Self { event } Self { event }
} }
}
impl NostrWidget for StreamEvent<'_> { pub fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
ui.vertical(|ui| { ui.vertical(|ui| {
ui.style_mut().spacing.item_spacing = Vec2::new(12., 16.); ui.style_mut().spacing.item_spacing = Vec2::new(12., 16.);
@ -127,8 +126,4 @@ impl NostrWidget for StreamEvent<'_> {
}) })
.response .response
} }
fn update(&mut self, _services: &mut RouteServices<'_, '_>) -> anyhow::Result<()> {
Ok(())
}
} }

View File

@ -13,10 +13,7 @@ impl<'a> StreamTitle<'a> {
pub fn new(event: &'a Note<'a>) -> StreamTitle<'a> { pub fn new(event: &'a Note<'a>) -> StreamTitle<'a> {
StreamTitle { event } StreamTitle { event }
} }
} pub fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
impl NostrWidget for StreamTitle<'_> {
fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
Frame::none() Frame::none()
.outer_margin(Margin::symmetric(12., 8.)) .outer_margin(Margin::symmetric(12., 8.))
.show(ui, |ui| { .show(ui, |ui| {
@ -43,8 +40,4 @@ impl NostrWidget for StreamTitle<'_> {
}) })
.response .response
} }
fn update(&mut self, _services: &mut RouteServices<'_, '_>) -> anyhow::Result<()> {
Ok(())
}
} }

View File

@ -1,12 +1,10 @@
use crate::link::NostrLink; use crate::link::NostrLink;
use crate::route::RouteServices; use crate::route::RouteServices;
use crate::theme::{MARGIN_DEFAULT, NEUTRAL_900, ROUNDING_DEFAULT}; use crate::theme::{MARGIN_DEFAULT, NEUTRAL_900, ROUNDING_DEFAULT};
use crate::widgets::{NativeTextInput, NostrWidget}; use crate::widgets::NativeTextInput;
use eframe::emath::Align; use eframe::emath::Align;
use egui::{Frame, Layout, Response, Sense, Ui, Widget}; use egui::{Frame, Layout, Response, Sense, Ui, Widget};
use log::info; use log::info;
use nostrdb::Filter;
use notedeck::AppContext;
pub struct WriteChat { pub struct WriteChat {
link: NostrLink, link: NostrLink,
@ -20,10 +18,8 @@ impl WriteChat {
msg: String::new(), msg: String::new(),
} }
} }
}
impl NostrWidget for WriteChat { pub fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
let logo_bytes = include_bytes!("../resources/send-03.svg"); let logo_bytes = include_bytes!("../resources/send-03.svg");
Frame::none() Frame::none()
.inner_margin(MARGIN_DEFAULT) .inner_margin(MARGIN_DEFAULT)
@ -52,8 +48,4 @@ impl NostrWidget for WriteChat {
}) })
.response .response
} }
fn update(&mut self, _services: &mut RouteServices<'_, '_>) -> anyhow::Result<()> {
Ok(())
}
} }