feat: defer image load
This commit is contained in:
parent
10cd15d942
commit
621686564d
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ab_glyph"
|
name = "ab_glyph"
|
||||||
@ -1379,7 +1379,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-video"
|
name = "egui-video"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
source = "git+https://github.com/v0l/egui-video.git?rev=e01405c9771e523a04ba36ffc5a08876ac0b274a#e01405c9771e523a04ba36ffc5a08876ac0b274a"
|
source = "git+https://github.com/v0l/egui-video.git?rev=bb3f6b83ba3a0619b1b9de0d4da88acb7fafd257#bb3f6b83ba3a0619b1b9de0d4da88acb7fafd257"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"atomic",
|
"atomic",
|
||||||
@ -1389,7 +1389,6 @@ dependencies = [
|
|||||||
"ffmpeg-rs-raw",
|
"ffmpeg-rs-raw",
|
||||||
"log",
|
"log",
|
||||||
"nom",
|
"nom",
|
||||||
"url",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1605,11 +1604,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ffmpeg-rs-raw"
|
name = "ffmpeg-rs-raw"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.v0l.io/Kieran/ffmpeg-rs-raw.git?rev=bc39a2ad99ad6489fa1525b9ad4fbca5dbf9cd07#bc39a2ad99ad6489fa1525b9ad4fbca5dbf9cd07"
|
source = "git+https://git.v0l.io/Kieran/ffmpeg-rs-raw.git?rev=8b6166f1db18ffb322a5a634d6b6deaddb79ecbf#8b6166f1db18ffb322a5a634d6b6deaddb79ecbf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ffmpeg-sys-the-third",
|
"ffmpeg-sys-the-third",
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"slimbox",
|
"slimbox",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ resvg = { version = "0.44.0", default-features = false }
|
|||||||
serde = { version = "1.0.214", features = ["derive"] }
|
serde = { version = "1.0.214", features = ["derive"] }
|
||||||
serde_with = { version = "3.11.0", features = ["hex"] }
|
serde_with = { version = "3.11.0", features = ["hex"] }
|
||||||
|
|
||||||
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "e01405c9771e523a04ba36ffc5a08876ac0b274a" }
|
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "bb3f6b83ba3a0619b1b9de0d4da88acb7fafd257" }
|
||||||
#egui-video = { path = "../egui-video" }
|
#egui-video = { path = "../egui-video" }
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::app::NativeLayerOps;
|
use crate::app::NativeLayerOps;
|
||||||
use crate::link::NostrLink;
|
use crate::link::NostrLink;
|
||||||
use crate::login::{Login, LoginKind};
|
use crate::login::Login;
|
||||||
use crate::note_util::OwnedNote;
|
use crate::note_util::OwnedNote;
|
||||||
use crate::route::home::HomePage;
|
use crate::route::home::HomePage;
|
||||||
use crate::route::login::LoginPage;
|
use crate::route::login::LoginPage;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use egui::ColorImage;
|
use egui::ColorImage;
|
||||||
use egui_video::ffmpeg_rs_raw::{Decoder, Demuxer, Scaler};
|
use egui_video::ffmpeg_rs_raw::{get_frame_from_hw, Decoder, Demuxer, Scaler};
|
||||||
use egui_video::ffmpeg_sys_the_third::{av_frame_free, av_packet_free, AVPixelFormat};
|
use egui_video::ffmpeg_sys_the_third::{av_frame_free, av_packet_free, AVPixelFormat};
|
||||||
use egui_video::media_player::video_frame_to_image;
|
use egui_video::media_player::video_frame_to_image;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -36,25 +36,23 @@ impl FfmpegLoader {
|
|||||||
anyhow::bail!("Not a video/image");
|
anyhow::bail!("Not a video/image");
|
||||||
};
|
};
|
||||||
let mut decode = Decoder::new();
|
let mut decode = Decoder::new();
|
||||||
let rgb = AVPixelFormat::AV_PIX_FMT_RGB24;
|
let rgb = AVPixelFormat::AV_PIX_FMT_RGBA;
|
||||||
let mut scaler = Scaler::new(rgb);
|
let mut scaler = Scaler::new();
|
||||||
|
|
||||||
decode.setup_decoder(bv, None)?;
|
decode.setup_decoder(bv, None)?;
|
||||||
|
|
||||||
let mut n_pkt = 0;
|
let mut n_pkt = 0;
|
||||||
loop {
|
loop {
|
||||||
let (mut pkt, stream) = demuxer.get_packet()?;
|
let (mut pkt, stream) = demuxer.get_packet()?;
|
||||||
if pkt.is_null() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (*stream).index as usize == bv.index {
|
if (*stream).index as usize == bv.index {
|
||||||
let frames = decode.decode_pkt(pkt, stream)?;
|
let frames = decode.decode_pkt(pkt, stream)?;
|
||||||
if let Some((frame, _)) = frames.first() {
|
if let Some((frame, _)) = frames.first() {
|
||||||
let mut frame = *frame;
|
let mut frame = get_frame_from_hw(*frame)?;
|
||||||
let frame_rgb = scaler.process_frame(
|
let frame_rgb = scaler.process_frame(
|
||||||
frame,
|
frame,
|
||||||
(*frame).width as u16,
|
(*frame).width as u16,
|
||||||
(*frame).height as u16,
|
(*frame).height as u16,
|
||||||
|
rgb,
|
||||||
)?;
|
)?;
|
||||||
av_frame_free(&mut frame);
|
av_frame_free(&mut frame);
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::services::ffmpeg_loader::FfmpegLoader;
|
use crate::services::ffmpeg_loader::FfmpegLoader;
|
||||||
use crate::theme::NEUTRAL_800;
|
use crate::theme::NEUTRAL_800;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use eframe::epaint::Color32;
|
use egui::{ColorImage, Context, Image, ImageData, TextureHandle, TextureOptions};
|
||||||
use egui::load::SizedTexture;
|
|
||||||
use egui::{ColorImage, Context, Image, ImageData, SizeHint, TextureHandle, TextureOptions};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
@ -13,7 +11,6 @@ use sha2::{Digest, Sha256};
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@ -62,7 +59,7 @@ impl ImageCache {
|
|||||||
if url.ends_with(".svg") {
|
if url.ends_with(".svg") {
|
||||||
Self::load_svg(bytes)
|
Self::load_svg(bytes)
|
||||||
} else {
|
} else {
|
||||||
let mut loader = FfmpegLoader::new();
|
let loader = FfmpegLoader::new();
|
||||||
loader.load_image_bytes(url, bytes)
|
loader.load_image_bytes(url, bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,7 +135,7 @@ impl ImageCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn load_image(ctx: &Context, path: PathBuf, key: &str) -> Option<TextureHandle> {
|
async fn load_image(ctx: &Context, path: PathBuf, key: &str) -> Option<TextureHandle> {
|
||||||
let mut loader = FfmpegLoader::new();
|
let loader = FfmpegLoader::new();
|
||||||
match loader.load_image(path) {
|
match loader.load_image(path) {
|
||||||
Ok(i) => Some(ctx.load_texture(key, ImageData::from(i), TextureOptions::default())),
|
Ok(i) => Some(ctx.load_texture(key, ImageData::from(i), TextureOptions::default())),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -153,7 +150,7 @@ impl ImageCache {
|
|||||||
use resvg::usvg::{Options, Tree};
|
use resvg::usvg::{Options, Tree};
|
||||||
|
|
||||||
let opt = Options::default();
|
let opt = Options::default();
|
||||||
let mut rtree = Tree::from_data(svg, &opt)
|
let rtree = Tree::from_data(svg, &opt)
|
||||||
.map_err(|err| err.to_string())
|
.map_err(|err| err.to_string())
|
||||||
.map_err(|e| Error::msg(e))?;
|
.map_err(|e| Error::msg(e))?;
|
||||||
|
|
||||||
|
@ -1,47 +1,42 @@
|
|||||||
use crate::route::RouteServices;
|
use crate::route::RouteServices;
|
||||||
use crate::services::image_cache::ImageCache;
|
|
||||||
use crate::services::ndb_wrapper::SubWrapper;
|
use crate::services::ndb_wrapper::SubWrapper;
|
||||||
use egui::{vec2, Color32, Image, Pos2, Response, Rounding, Sense, Ui, Vec2, Widget};
|
use egui::{vec2, Color32, Pos2, Response, Rounding, Sense, Ui, Vec2, Widget};
|
||||||
use nostrdb::NdbProfile;
|
use nostrdb::NdbProfile;
|
||||||
|
|
||||||
pub struct Avatar<'a> {
|
pub struct Avatar<'a> {
|
||||||
image: Option<Image<'a>>,
|
image: Option<&'a str>,
|
||||||
sub: Option<SubWrapper>,
|
sub: Option<SubWrapper>,
|
||||||
size: Option<f32>,
|
size: Option<f32>,
|
||||||
|
services: &'a RouteServices<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Avatar<'a> {
|
impl<'a> Avatar<'a> {
|
||||||
pub fn new(img: Image<'a>) -> Self {
|
pub fn new_optional(img: Option<&'a str>, services: &'a RouteServices<'a>) -> Self {
|
||||||
Self {
|
|
||||||
image: Some(img),
|
|
||||||
sub: None,
|
|
||||||
size: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_optional(img: Option<Image<'a>>) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
image: img,
|
image: img,
|
||||||
sub: None,
|
sub: None,
|
||||||
size: None,
|
size: None,
|
||||||
|
services,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_profile(p: &'a Option<NdbProfile<'a>>, svc: &'a ImageCache) -> Self {
|
pub fn from_profile(p: &'a Option<NdbProfile<'a>>, services: &'a RouteServices<'a>) -> Self {
|
||||||
let img = p.map_or(None, |f| f.picture().map(|f| svc.load(f)));
|
let img = p.map(|f| f.picture()).unwrap_or(None);
|
||||||
Self {
|
Self {
|
||||||
image: img,
|
image: img,
|
||||||
sub: None,
|
sub: None,
|
||||||
size: None,
|
size: None,
|
||||||
|
services,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pubkey(pk: &[u8; 32], svc: &'a RouteServices<'a>) -> Self {
|
pub fn pubkey(pk: &[u8; 32], services: &'a RouteServices<'a>) -> Self {
|
||||||
let (p, sub) = svc.ndb.fetch_profile(svc.tx, pk);
|
let (p, sub) = services.ndb.fetch_profile(services.tx, pk);
|
||||||
Self {
|
Self {
|
||||||
image: p.and_then(|p| p.picture().map(|p| svc.img_cache.load(p))),
|
image: p.map(|f| f.picture()).unwrap_or(None),
|
||||||
sub,
|
sub,
|
||||||
size: None,
|
size: None,
|
||||||
|
services,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,10 +60,14 @@ impl<'a> Widget for Avatar<'a> {
|
|||||||
fn ui(self, ui: &mut Ui) -> Response {
|
fn ui(self, ui: &mut Ui) -> Response {
|
||||||
let size_v = self.size.unwrap_or(40.);
|
let size_v = self.size.unwrap_or(40.);
|
||||||
let size = Vec2::new(size_v, size_v);
|
let size = Vec2::new(size_v, size_v);
|
||||||
if !ui.is_rect_visible(ui.cursor()) {
|
if !ui.is_visible() {
|
||||||
return Self::placeholder(ui, size_v);
|
return Self::placeholder(ui, size_v);
|
||||||
}
|
}
|
||||||
match self.image {
|
match self
|
||||||
|
.image
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| self.services.img_cache.load(*i))
|
||||||
|
{
|
||||||
Some(img) => img
|
Some(img) => img
|
||||||
.fit_to_exact_size(size)
|
.fit_to_exact_size(size)
|
||||||
.rounding(Rounding::same(size_v))
|
.rounding(Rounding::same(size_v))
|
||||||
|
@ -4,7 +4,7 @@ use crate::route::RouteServices;
|
|||||||
use crate::services::ndb_wrapper::{NDBWrapper, SubWrapper};
|
use crate::services::ndb_wrapper::{NDBWrapper, SubWrapper};
|
||||||
use crate::widgets::chat_message::ChatMessage;
|
use crate::widgets::chat_message::ChatMessage;
|
||||||
use crate::widgets::NostrWidget;
|
use crate::widgets::NostrWidget;
|
||||||
use egui::{Frame, Margin, Response, ScrollArea, Ui, Widget};
|
use egui::{Frame, Margin, Response, ScrollArea, Ui};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nostrdb::{Filter, Note, NoteKey, Transaction};
|
use nostrdb::{Filter, Note, NoteKey, Transaction};
|
||||||
|
|
||||||
@ -68,10 +68,11 @@ impl NostrWidget for Chat {
|
|||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.spacing_mut().item_spacing.y = 8.0;
|
ui.spacing_mut().item_spacing.y = 8.0;
|
||||||
for ev in events
|
for ev in events
|
||||||
.iter()
|
.into_iter()
|
||||||
.sorted_by(|a, b| a.created_at().cmp(&b.created_at()))
|
.sorted_by(|a, b| a.created_at().cmp(&b.created_at()))
|
||||||
{
|
{
|
||||||
ChatMessage::new(&stream, ev, services).ui(ui);
|
let c = ChatMessage::new(&stream, &ev, services);
|
||||||
|
ui.add(c);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -57,7 +57,7 @@ impl<'a> Widget for ChatMessage<'a> {
|
|||||||
format.color = Color32::WHITE;
|
format.color = Color32::WHITE;
|
||||||
job.append(self.ev.content(), 5.0, format.clone());
|
job.append(self.ev.content(), 5.0, format.clone());
|
||||||
|
|
||||||
ui.add(Avatar::from_profile(&profile, self.services.img_cache).size(24.));
|
ui.add(Avatar::from_profile(&profile, self.services).size(24.));
|
||||||
ui.add(Label::new(job).wrap_mode(TextWrapMode::Wrap));
|
ui.add(Label::new(job).wrap_mode(TextWrapMode::Wrap));
|
||||||
})
|
})
|
||||||
.response
|
.response
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use crate::login::LoginKind;
|
|
||||||
use crate::route::{RouteServices, Routes};
|
use crate::route::{RouteServices, Routes};
|
||||||
use crate::widgets::avatar::Avatar;
|
use crate::widgets::avatar::Avatar;
|
||||||
use crate::widgets::{Button, NostrWidget};
|
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, Image, Layout, Margin, Response, Sense, Ui, Widget};
|
use egui::{CursorIcon, Frame, Layout, Margin, Response, Sense, Ui, Widget};
|
||||||
|
|
||||||
pub struct Header;
|
pub struct Header;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ pub struct Profile<'a> {
|
|||||||
profile: Option<NdbProfile<'a>>,
|
profile: Option<NdbProfile<'a>>,
|
||||||
sub: Option<SubWrapper>,
|
sub: Option<SubWrapper>,
|
||||||
img_cache: &'a ImageCache,
|
img_cache: &'a ImageCache,
|
||||||
|
services: &'a RouteServices<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Profile<'a> {
|
impl<'a> Profile<'a> {
|
||||||
@ -24,6 +25,7 @@ impl<'a> Profile<'a> {
|
|||||||
profile: p,
|
profile: p,
|
||||||
img_cache: services.img_cache,
|
img_cache: services.img_cache,
|
||||||
sub,
|
sub,
|
||||||
|
services,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +39,7 @@ impl<'a> Widget for Profile<'a> {
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.spacing_mut().item_spacing.x = 8.;
|
ui.spacing_mut().item_spacing.x = 8.;
|
||||||
|
|
||||||
ui.add(Avatar::from_profile(&self.profile, self.img_cache).size(self.size));
|
ui.add(Avatar::from_profile(&self.profile, self.services).size(self.size));
|
||||||
ui.add(Username::new(&self.profile, FONT_SIZE))
|
ui.add(Username::new(&self.profile, FONT_SIZE))
|
||||||
})
|
})
|
||||||
.response
|
.response
|
||||||
|
@ -16,7 +16,7 @@ impl<'a> StreamList<'a> {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
id: egui::Id,
|
id: egui::Id,
|
||||||
streams: &'a NoteStore<'a>,
|
streams: &'a NoteStore<'a>,
|
||||||
services: &'a RouteServices,
|
services: &'a RouteServices<'a>,
|
||||||
heading: Option<impl Into<WidgetText>>,
|
heading: Option<impl Into<WidgetText>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::widgets::PlaceholderRect;
|
use crate::widgets::PlaceholderRect;
|
||||||
use egui::{Context, Response, Ui, Vec2, Widget};
|
use egui::{Context, Response, Ui, Widget};
|
||||||
use egui_video::{Player, PlayerControls};
|
use egui_video::{Player, PlayerControls};
|
||||||
|
|
||||||
pub struct StreamPlayer {
|
pub struct StreamPlayer {
|
||||||
|
@ -32,10 +32,15 @@ impl Widget for StreamEvent<'_> {
|
|||||||
|
|
||||||
let w = ui.available_width();
|
let w = ui.available_width();
|
||||||
let h = (w / 16.0) * 9.0;
|
let h = (w / 16.0) * 9.0;
|
||||||
let cover = self.event.image().map(|p| self.services.img_cache.load(p));
|
|
||||||
|
|
||||||
let (response, painter) = ui.allocate_painter(Vec2::new(w, h), Sense::click());
|
let (response, painter) = ui.allocate_painter(Vec2::new(w, h), Sense::click());
|
||||||
|
|
||||||
|
let cover = if ui.is_visible() {
|
||||||
|
self.event.image().map(|p| self.services.img_cache.load(p))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(cover) = cover.map(|c| {
|
if let Some(cover) = cover.map(|c| {
|
||||||
c.rounding(Rounding::same(12.))
|
c.rounding(Rounding::same(12.))
|
||||||
.load_for_size(painter.ctx(), Vec2::new(w, h))
|
.load_for_size(painter.ctx(), Vec2::new(w, h))
|
||||||
@ -111,7 +116,7 @@ impl Widget for StreamEvent<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add(Avatar::from_profile(&host_profile, self.services.img_cache).size(40.));
|
ui.add(Avatar::from_profile(&host_profile, self.services).size(40.));
|
||||||
let title = RichText::new(self.event.title().unwrap_or("Untitled"))
|
let title = RichText::new(self.event.title().unwrap_or("Untitled"))
|
||||||
.size(16.)
|
.size(16.)
|
||||||
.color(Color32::WHITE);
|
.color(Color32::WHITE);
|
||||||
|
@ -2,7 +2,7 @@ use crate::note_util::NoteUtil;
|
|||||||
use crate::route::RouteServices;
|
use crate::route::RouteServices;
|
||||||
use crate::stream_info::StreamInfo;
|
use crate::stream_info::StreamInfo;
|
||||||
use crate::widgets::{NostrWidget, Profile};
|
use crate::widgets::{NostrWidget, Profile};
|
||||||
use egui::{Color32, Frame, Label, Margin, Response, RichText, TextWrapMode, Ui, Widget};
|
use egui::{Color32, Frame, Label, Margin, Response, RichText, TextWrapMode, Ui};
|
||||||
use nostrdb::Note;
|
use nostrdb::Note;
|
||||||
|
|
||||||
pub struct StreamTitle<'a> {
|
pub struct StreamTitle<'a> {
|
||||||
@ -26,7 +26,7 @@ impl<'a> NostrWidget for StreamTitle<'a> {
|
|||||||
.color(Color32::WHITE);
|
.color(Color32::WHITE);
|
||||||
ui.add(Label::new(title.strong()).wrap_mode(TextWrapMode::Truncate));
|
ui.add(Label::new(title.strong()).wrap_mode(TextWrapMode::Truncate));
|
||||||
|
|
||||||
Profile::new(self.event.host(), services).size(32.).ui(ui);
|
ui.add(Profile::new(self.event.host(), services).size(32.));
|
||||||
|
|
||||||
if let Some(summary) = self
|
if let Some(summary) = self
|
||||||
.event
|
.event
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::link::NostrLink;
|
use crate::link::NostrLink;
|
||||||
use crate::route::{RouteAction, 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, NostrWidget};
|
||||||
use eframe::emath::Align;
|
use eframe::emath::Align;
|
||||||
use egui::{Frame, Image, Layout, Margin, Response, Rounding, Sense, Stroke, TextEdit, Ui, Widget};
|
use egui::{Frame, Layout, Response, Sense, Ui, Widget};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
pub struct WriteChat {
|
pub struct WriteChat {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user