wip
This commit is contained in:
parent
0e19c1a8f3
commit
f7021094bc
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
45
src/app.rs
45
src/app.rs
@ -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()],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
30
src/profiles.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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(),
|
||||||
);
|
);
|
||||||
|
@ -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
|
||||||
|
.and_then(|img| {
|
||||||
|
std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap();
|
||||||
std::fs::write(&cache_path, &img.bytes).unwrap();
|
std::fs::write(&cache_path, &img.bytes).unwrap();
|
||||||
let img_loaded = FfmpegLoader::new().load_image(cache_path).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();
|
||||||
|
@ -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)?;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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 };
|
||||||
|
|
||||||
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user