Fullscreen MediaViewer refactor
- Moved media related logic into notedeck instead of the ui crate, since they pertain to Images/ImageCache based systems - Made RenderableMedia owned to make it less of a nightmware to work with and the perf should be negligible - Added a ImageMetadata cache to Images. This is referenced whenever we encounter an image so we don't have to redo the work all of the time - Relpaced our ad-hoc, hand(vibe?)-coded panning and zoom logic with the Scene widget, which is explicitly designed for this use case - Extracted and detangle fullscreen media rendering from inside of note rendering. We instead let the application decide what action they want to perform when note media is clicked on. - We add an on_view_media action to MediaAction for the application to handle. The Columns app uses this toggle a FullscreenMedia app option bits whenever we get a MediaAction::ViewMedis(urls). Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
column::Columns,
|
||||
nav::{RouterAction, RouterType},
|
||||
options::AppOptions,
|
||||
route::Route,
|
||||
timeline::{
|
||||
thread::{
|
||||
@@ -8,6 +9,7 @@ use crate::{
|
||||
},
|
||||
ThreadSelection, TimelineCache, TimelineKind,
|
||||
},
|
||||
view_state::ViewState,
|
||||
};
|
||||
|
||||
use enostr::{NoteId, Pubkey, RelayPool};
|
||||
@@ -51,6 +53,8 @@ fn execute_note_action(
|
||||
global_wallet: &mut GlobalWallet,
|
||||
zaps: &mut Zaps,
|
||||
images: &mut Images,
|
||||
view_state: &mut ViewState,
|
||||
app_options: &mut AppOptions,
|
||||
router_type: RouterType,
|
||||
ui: &mut egui::Ui,
|
||||
col: usize,
|
||||
@@ -153,7 +157,12 @@ fn execute_note_action(
|
||||
}
|
||||
},
|
||||
NoteAction::Media(media_action) => {
|
||||
media_action.process(images);
|
||||
media_action.on_view_media(|medias| {
|
||||
view_state.media_viewer.urls = medias;
|
||||
app_options.set(AppOptions::FullscreenMedia, true);
|
||||
});
|
||||
|
||||
media_action.process_default_media_actions(images)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +189,8 @@ pub fn execute_and_process_note_action(
|
||||
global_wallet: &mut GlobalWallet,
|
||||
zaps: &mut Zaps,
|
||||
images: &mut Images,
|
||||
view_state: &mut ViewState,
|
||||
app_options: &mut AppOptions,
|
||||
ui: &mut egui::Ui,
|
||||
) -> Option<RouterAction> {
|
||||
let router_type = {
|
||||
@@ -204,6 +215,8 @@ pub fn execute_and_process_note_action(
|
||||
global_wallet,
|
||||
zaps,
|
||||
images,
|
||||
view_state,
|
||||
app_options,
|
||||
router_type,
|
||||
ui,
|
||||
col,
|
||||
|
||||
@@ -20,9 +20,12 @@ use enostr::{ClientMessage, PoolRelay, Pubkey, RelayEvent, RelayMessage, RelayPo
|
||||
use nostrdb::Transaction;
|
||||
use notedeck::{
|
||||
tr, ui::is_narrow, Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState,
|
||||
Localization, UnknownIds,
|
||||
Images, JobsCache, Localization, UnknownIds,
|
||||
};
|
||||
use notedeck_ui::{
|
||||
media::{MediaViewer, MediaViewerState},
|
||||
NoteOptions,
|
||||
};
|
||||
use notedeck_ui::{jobs::JobsCache, NoteOptions};
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
@@ -365,12 +368,43 @@ fn render_damus(
|
||||
render_damus_desktop(damus, app_ctx, ui)
|
||||
};
|
||||
|
||||
fullscreen_media_viewer_ui(
|
||||
ui,
|
||||
&mut damus.options,
|
||||
&mut damus.view_state.media_viewer,
|
||||
app_ctx.img_cache,
|
||||
);
|
||||
|
||||
// We use this for keeping timestamps and things up to date
|
||||
ui.ctx().request_repaint_after(Duration::from_secs(5));
|
||||
|
||||
app_action
|
||||
}
|
||||
|
||||
/// Present a fullscreen media viewer if the FullscreenMedia AppOptions flag is set. This is
|
||||
/// typically set by image carousels using a MediaAction's on_view_media callback when
|
||||
/// an image is clicked
|
||||
fn fullscreen_media_viewer_ui(
|
||||
ui: &mut egui::Ui,
|
||||
options: &mut AppOptions,
|
||||
viewer_state: &mut MediaViewerState,
|
||||
img_cache: &mut Images,
|
||||
) {
|
||||
if !options.contains(AppOptions::FullscreenMedia) || viewer_state.urls.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Close it?
|
||||
if ui.input(|i| i.key_pressed(egui::Key::Escape)) {
|
||||
options.set(AppOptions::FullscreenMedia, false);
|
||||
return;
|
||||
}
|
||||
|
||||
MediaViewer::new(viewer_state)
|
||||
.fullscreen(true)
|
||||
.ui(img_cache, ui);
|
||||
}
|
||||
|
||||
/*
|
||||
fn determine_key_storage_type() -> KeyStorageType {
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -11,7 +11,7 @@ use sha2::{Digest, Sha256};
|
||||
use url::Url;
|
||||
|
||||
use crate::Error;
|
||||
use notedeck_ui::images::fetch_binary_from_disk;
|
||||
use notedeck::media::images::fetch_binary_from_disk;
|
||||
|
||||
pub const NOSTR_BUILD_URL: fn() -> Url = || Url::parse("http://nostr.build").unwrap();
|
||||
const NIP96_WELL_KNOWN: &str = ".well-known/nostr/nip96.json";
|
||||
@@ -143,7 +143,7 @@ pub fn nip96_upload(
|
||||
Err(e) => {
|
||||
return Promise::from_ready(Err(Error::Generic(format!(
|
||||
"could not read contents of file to upload: {e}"
|
||||
))))
|
||||
))));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -459,6 +459,8 @@ fn process_render_nav_action(
|
||||
ctx.global_wallet,
|
||||
ctx.zaps,
|
||||
ctx.img_cache,
|
||||
&mut app.view_state,
|
||||
&mut app.options,
|
||||
ui,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ bitflags! {
|
||||
|
||||
/// Should we scroll to top on the active column?
|
||||
const ScrollToTop = 1 << 3;
|
||||
|
||||
/// Are we showing fullscreen media?
|
||||
const FullscreenMedia = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::{
|
||||
};
|
||||
|
||||
use enostr::Pubkey;
|
||||
use notedeck::NoteContext;
|
||||
use notedeck_ui::{jobs::JobsCache, NoteOptions};
|
||||
use notedeck::{JobsCache, NoteContext};
|
||||
use notedeck_ui::NoteOptions;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn render_timeline_route(
|
||||
|
||||
@@ -14,13 +14,12 @@ use egui::{
|
||||
};
|
||||
use enostr::{FilledKeypair, FullKeypair, NoteId, Pubkey, RelayPool};
|
||||
use nostrdb::{Ndb, Transaction};
|
||||
use notedeck::media::gif::ensure_latest_texture;
|
||||
use notedeck::{get_render_state, JobsCache, PixelDimensions, RenderState};
|
||||
|
||||
use notedeck_ui::{
|
||||
app_images,
|
||||
blur::PixelDimensions,
|
||||
context_menu::{input_context, PasteBehavior},
|
||||
gif::{handle_repaint, retrieve_latest_texture},
|
||||
images::{get_render_state, RenderState},
|
||||
jobs::JobsCache,
|
||||
note::render_note_preview,
|
||||
NoteOptions, ProfilePic,
|
||||
};
|
||||
@@ -471,7 +470,7 @@ impl<'a, 'd> PostView<'a, 'd> {
|
||||
self.note_context.img_cache,
|
||||
cache_type,
|
||||
url,
|
||||
notedeck_ui::images::ImageType::Content(Some((width, height))),
|
||||
notedeck::ImageType::Content(Some((width, height))),
|
||||
);
|
||||
|
||||
render_post_view_media(
|
||||
@@ -595,12 +594,10 @@ fn render_post_view_media(
|
||||
.to_points(ui.pixels_per_point())
|
||||
.to_vec();
|
||||
|
||||
let texture_handle = handle_repaint(
|
||||
ui,
|
||||
retrieve_latest_texture(url, render_state.gifs, renderable_media),
|
||||
);
|
||||
let texture_handle =
|
||||
ensure_latest_texture(ui, url, render_state.gifs, renderable_media);
|
||||
let img_resp = ui.add(
|
||||
egui::Image::new(texture_handle)
|
||||
egui::Image::new(&texture_handle)
|
||||
.max_size(size)
|
||||
.corner_radius(12.0),
|
||||
);
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::{
|
||||
|
||||
use egui::ScrollArea;
|
||||
use enostr::{FilledKeypair, NoteId};
|
||||
use notedeck::NoteContext;
|
||||
use notedeck_ui::{jobs::JobsCache, NoteOptions};
|
||||
use notedeck::{JobsCache, NoteContext};
|
||||
use notedeck_ui::NoteOptions;
|
||||
|
||||
pub struct QuoteRepostView<'a, 'd> {
|
||||
note_context: &'a mut NoteContext<'d>,
|
||||
|
||||
@@ -6,8 +6,7 @@ use crate::ui::{
|
||||
|
||||
use egui::{Rect, Response, ScrollArea, Ui};
|
||||
use enostr::{FilledKeypair, NoteId};
|
||||
use notedeck::NoteContext;
|
||||
use notedeck_ui::jobs::JobsCache;
|
||||
use notedeck::{JobsCache, NoteContext};
|
||||
use notedeck_ui::{NoteOptions, NoteView, ProfilePic};
|
||||
|
||||
pub struct PostReplyView<'a, 'd> {
|
||||
|
||||
@@ -13,12 +13,11 @@ use crate::{
|
||||
ui::timeline::{tabs_ui, TimelineTabView},
|
||||
};
|
||||
use notedeck::{
|
||||
name::get_display_name, profile::get_profile_url, IsFollowing, NoteAction, NoteContext,
|
||||
NotedeckTextStyle,
|
||||
name::get_display_name, profile::get_profile_url, IsFollowing, JobsCache, NoteAction,
|
||||
NoteContext, NotedeckTextStyle,
|
||||
};
|
||||
use notedeck_ui::{
|
||||
app_images,
|
||||
jobs::JobsCache,
|
||||
profile::{about_section_widget, banner, display_name_widget},
|
||||
NoteOptions, ProfilePic,
|
||||
};
|
||||
|
||||
@@ -5,11 +5,11 @@ use state::TypingType;
|
||||
use crate::{timeline::TimelineTab, ui::timeline::TimelineTabView};
|
||||
use egui_winit::clipboard::Clipboard;
|
||||
use nostrdb::{Filter, Ndb, Transaction};
|
||||
use notedeck::{tr, tr_plural, Localization, NoteAction, NoteContext, NoteRef};
|
||||
use notedeck::{tr, tr_plural, JobsCache, Localization, NoteAction, NoteContext, NoteRef};
|
||||
|
||||
use notedeck_ui::{
|
||||
context_menu::{input_context, PasteBehavior},
|
||||
icons::search_icon,
|
||||
jobs::JobsCache,
|
||||
padding, NoteOptions,
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -2,8 +2,8 @@ use egui::InnerResponse;
|
||||
use egui_virtual_list::VirtualList;
|
||||
use nostrdb::{Note, Transaction};
|
||||
use notedeck::note::root_note_id_from_selected_id;
|
||||
use notedeck::JobsCache;
|
||||
use notedeck::{NoteAction, NoteContext};
|
||||
use notedeck_ui::jobs::JobsCache;
|
||||
use notedeck_ui::note::NoteResponse;
|
||||
use notedeck_ui::{NoteOptions, NoteView};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use egui::{vec2, Direction, Layout, Pos2, Stroke};
|
||||
use egui_tabs::TabColor;
|
||||
use nostrdb::Transaction;
|
||||
use notedeck::ui::is_narrow;
|
||||
use notedeck_ui::jobs::JobsCache;
|
||||
use notedeck::JobsCache;
|
||||
use std::f32::consts::PI;
|
||||
use tracing::{error, warn};
|
||||
|
||||
|
||||
@@ -6,8 +6,12 @@ use crate::deck_state::DeckState;
|
||||
use crate::login_manager::AcquireKeyState;
|
||||
use crate::ui::search::SearchQueryState;
|
||||
use enostr::ProfileState;
|
||||
use notedeck_ui::media::MediaViewerState;
|
||||
|
||||
/// Various state for views
|
||||
///
|
||||
/// TODO(jb55): we likely want to encapsulate these better,
|
||||
/// or at least document where they are used
|
||||
#[derive(Default)]
|
||||
pub struct ViewState {
|
||||
pub login: AcquireKeyState,
|
||||
@@ -16,6 +20,11 @@ pub struct ViewState {
|
||||
pub id_string_map: HashMap<egui::Id, String>,
|
||||
pub searches: HashMap<egui::Id, SearchQueryState>,
|
||||
pub pubkey_to_profile_state: HashMap<Pubkey, ProfileState>,
|
||||
|
||||
/// Keeps track of what urls we are actively viewing in the
|
||||
/// fullscreen media viewier, as well as any other state we want to
|
||||
/// keep track of
|
||||
pub media_viewer: MediaViewerState,
|
||||
}
|
||||
|
||||
impl ViewState {
|
||||
|
||||
Reference in New Issue
Block a user