mirror of
https://github.com/aljazceru/notedeck.git
synced 2025-12-18 09:04:21 +01:00
@@ -13,18 +13,183 @@ use std::time::{Duration, Instant, SystemTime};
|
|||||||
|
|
||||||
use hex::ToHex;
|
use hex::ToHex;
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use std::path;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::path::{self};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
pub type MediaCacheValue = Promise<Result<TexturedImage>>;
|
pub type MediaCacheValue = Promise<Result<TexturedImage>>;
|
||||||
pub type MediaCacheMap = HashMap<String, MediaCacheValue>;
|
pub type MediaCacheMap = HashMap<String, MediaCacheValue>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TexturesCache {
|
||||||
|
cache: hashbrown::HashMap<String, TextureStateInternal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TexturesCache {
|
||||||
|
pub fn handle_and_get_or_insert_loadable(
|
||||||
|
&mut self,
|
||||||
|
url: &str,
|
||||||
|
closure: impl FnOnce() -> Promise<Option<Result<TexturedImage>>>,
|
||||||
|
) -> LoadableTextureState {
|
||||||
|
let internal = self.handle_and_get_state_internal(url, true, closure);
|
||||||
|
|
||||||
|
internal.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_and_get_or_insert(
|
||||||
|
&mut self,
|
||||||
|
url: &str,
|
||||||
|
|
||||||
|
closure: impl FnOnce() -> Promise<Option<Result<TexturedImage>>>,
|
||||||
|
) -> TextureState {
|
||||||
|
let internal = self.handle_and_get_state_internal(url, false, closure);
|
||||||
|
|
||||||
|
internal.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_and_get_state_internal(
|
||||||
|
&mut self,
|
||||||
|
url: &str,
|
||||||
|
use_loading: bool,
|
||||||
|
closure: impl FnOnce() -> Promise<Option<Result<TexturedImage>>>,
|
||||||
|
) -> &mut TextureStateInternal {
|
||||||
|
let state = match self.cache.raw_entry_mut().from_key(url) {
|
||||||
|
hashbrown::hash_map::RawEntryMut::Occupied(entry) => {
|
||||||
|
let state = entry.into_mut();
|
||||||
|
handle_occupied(state, use_loading);
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
|
||||||
|
let res = closure();
|
||||||
|
let (_, state) = entry.insert(url.to_owned(), TextureStateInternal::Pending(res));
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_pending(&mut self, url: &str, promise: Promise<Option<Result<TexturedImage>>>) {
|
||||||
|
self.cache
|
||||||
|
.insert(url.to_owned(), TextureStateInternal::Pending(promise));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_to_loaded(&mut self, url: &str) {
|
||||||
|
let hashbrown::hash_map::RawEntryMut::Occupied(entry) =
|
||||||
|
self.cache.raw_entry_mut().from_key(url)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
entry.replace_entry_with(|_, v| {
|
||||||
|
let TextureStateInternal::Loading(textured) = v else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(TextureStateInternal::Loaded(textured))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_and_handle(&mut self, url: &str) -> Option<LoadableTextureState> {
|
||||||
|
self.cache.get_mut(url).map(|state| {
|
||||||
|
handle_occupied(state, true);
|
||||||
|
state.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_occupied(state: &mut TextureStateInternal, use_loading: bool) {
|
||||||
|
let TextureStateInternal::Pending(promise) = state else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(res) = promise.ready_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(res) = res.take() else {
|
||||||
|
tracing::error!("Failed to take the promise");
|
||||||
|
*state =
|
||||||
|
TextureStateInternal::Error(crate::Error::Generic("Promise already taken".to_owned()));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(textured) => {
|
||||||
|
*state = if use_loading {
|
||||||
|
TextureStateInternal::Loading(textured)
|
||||||
|
} else {
|
||||||
|
TextureStateInternal::Loaded(textured)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => *state = TextureStateInternal::Error(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LoadableTextureState<'a> {
|
||||||
|
Pending,
|
||||||
|
Error(&'a crate::Error),
|
||||||
|
Loading {
|
||||||
|
actual_image_tex: &'a mut TexturedImage,
|
||||||
|
}, // the texture is in the loading state, for transitioning between the pending and loaded states
|
||||||
|
Loaded(&'a mut TexturedImage),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TextureState<'a> {
|
||||||
|
Pending,
|
||||||
|
Error(&'a crate::Error),
|
||||||
|
Loaded(&'a mut TexturedImage),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a mut TextureStateInternal> for TextureState<'a> {
|
||||||
|
fn from(value: &'a mut TextureStateInternal) -> Self {
|
||||||
|
match value {
|
||||||
|
TextureStateInternal::Pending(_) => TextureState::Pending,
|
||||||
|
TextureStateInternal::Error(error) => TextureState::Error(error),
|
||||||
|
TextureStateInternal::Loading(textured_image) => TextureState::Loaded(textured_image),
|
||||||
|
TextureStateInternal::Loaded(textured_image) => TextureState::Loaded(textured_image),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TextureStateInternal {
|
||||||
|
Pending(Promise<Option<Result<TexturedImage>>>),
|
||||||
|
Error(crate::Error),
|
||||||
|
Loading(TexturedImage), // the image is in the loading state, for transitioning between blur and image
|
||||||
|
Loaded(TexturedImage),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a mut TextureStateInternal> for LoadableTextureState<'a> {
|
||||||
|
fn from(value: &'a mut TextureStateInternal) -> Self {
|
||||||
|
match value {
|
||||||
|
TextureStateInternal::Pending(_) => LoadableTextureState::Pending,
|
||||||
|
TextureStateInternal::Error(error) => LoadableTextureState::Error(error),
|
||||||
|
TextureStateInternal::Loading(textured_image) => LoadableTextureState::Loading {
|
||||||
|
actual_image_tex: textured_image,
|
||||||
|
},
|
||||||
|
TextureStateInternal::Loaded(textured_image) => {
|
||||||
|
LoadableTextureState::Loaded(textured_image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum TexturedImage {
|
pub enum TexturedImage {
|
||||||
Static(TextureHandle),
|
Static(TextureHandle),
|
||||||
Animated(Animation),
|
Animated(Animation),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TexturedImage {
|
||||||
|
pub fn get_first_texture(&self) -> &TextureHandle {
|
||||||
|
match self {
|
||||||
|
TexturedImage::Static(texture_handle) => texture_handle,
|
||||||
|
TexturedImage::Animated(animation) => &animation.first_frame.texture,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Animation {
|
pub struct Animation {
|
||||||
pub first_frame: TextureFrame,
|
pub first_frame: TextureFrame,
|
||||||
pub other_frames: Vec<TextureFrame>,
|
pub other_frames: Vec<TextureFrame>,
|
||||||
@@ -60,7 +225,7 @@ pub struct MediaCache {
|
|||||||
url_imgs: MediaCacheMap,
|
url_imgs: MediaCacheMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
pub enum MediaCacheType {
|
pub enum MediaCacheType {
|
||||||
Image,
|
Image,
|
||||||
Gif,
|
Gif,
|
||||||
@@ -215,6 +380,20 @@ impl Images {
|
|||||||
self.static_imgs.migrate_v0()?;
|
self.static_imgs.migrate_v0()?;
|
||||||
self.gifs.migrate_v0()
|
self.gifs.migrate_v0()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_cache(&self, cache_type: MediaCacheType) -> &MediaCache {
|
||||||
|
match cache_type {
|
||||||
|
MediaCacheType::Image => &self.static_imgs,
|
||||||
|
MediaCacheType::Gif => &self.gifs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cache_mut(&mut self, cache_type: MediaCacheType) -> &mut MediaCache {
|
||||||
|
match cache_type {
|
||||||
|
MediaCacheType::Image => &mut self.static_imgs,
|
||||||
|
MediaCacheType::Gif => &mut self.gifs,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type GifStateMap = HashMap<String, GifState>;
|
pub type GifStateMap = HashMap<String, GifState>;
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ pub use error::{Error, FilterError, ZapError};
|
|||||||
pub use filter::{FilterState, FilterStates, UnifiedSubscription};
|
pub use filter::{FilterState, FilterStates, UnifiedSubscription};
|
||||||
pub use fonts::NamedFontFamily;
|
pub use fonts::NamedFontFamily;
|
||||||
pub use imgcache::{
|
pub use imgcache::{
|
||||||
Animation, GifState, GifStateMap, ImageFrame, Images, MediaCache, MediaCacheType,
|
Animation, GifState, GifStateMap, ImageFrame, Images, LoadableTextureState, MediaCache,
|
||||||
MediaCacheValue, TextureFrame, TexturedImage,
|
MediaCacheType, TextureFrame, TextureState, TexturedImage, TexturesCache,
|
||||||
};
|
};
|
||||||
pub use job_pool::JobPool;
|
pub use job_pool::JobPool;
|
||||||
pub use muted::{MuteFun, Muted};
|
pub use muted::{MuteFun, Muted};
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ fn render_media_cache(
|
|||||||
let m_cached_promise = cache.map().get(url);
|
let m_cached_promise = cache.map().get(url);
|
||||||
|
|
||||||
if m_cached_promise.is_none() {
|
if m_cached_promise.is_none() {
|
||||||
let res = crate::images::fetch_img(cache, ui.ctx(), url, img_type, cache_type.clone());
|
let res = crate::images::fetch_img(cache, ui.ctx(), url, img_type, cache_type);
|
||||||
cache.map_mut().insert(url.to_owned(), res);
|
cache.map_mut().insert(url.to_owned(), res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ fn image_carousel(
|
|||||||
ui.ctx().memory(|mem| {
|
ui.ctx().memory(|mem| {
|
||||||
mem.data
|
mem.data
|
||||||
.get_temp::<(String, MediaCacheType)>(carousel_id.with("current_image"))
|
.get_temp::<(String, MediaCacheType)>(carousel_id.with("current_image"))
|
||||||
.unwrap_or_else(|| (images[0].0.clone(), images[0].1.clone()))
|
.unwrap_or_else(|| (images[0].0.clone(), images[0].1))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ fn image_carousel(
|
|||||||
img_cache,
|
img_cache,
|
||||||
&image,
|
&image,
|
||||||
ImageType::Content,
|
ImageType::Content,
|
||||||
cache_type.clone(),
|
cache_type,
|
||||||
|ui| {
|
|ui| {
|
||||||
ui.allocate_space(egui::vec2(spinsz, spinsz));
|
ui.allocate_space(egui::vec2(spinsz, spinsz));
|
||||||
},
|
},
|
||||||
@@ -360,7 +360,7 @@ fn image_carousel(
|
|||||||
mem.data.insert_temp(carousel_id.with("show_popup"), true);
|
mem.data.insert_temp(carousel_id.with("show_popup"), true);
|
||||||
mem.data.insert_temp(
|
mem.data.insert_temp(
|
||||||
carousel_id.with("current_image"),
|
carousel_id.with("current_image"),
|
||||||
(image.clone(), cache_type.clone()),
|
(image.clone(), cache_type),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -438,7 +438,7 @@ fn image_carousel(
|
|||||||
img_cache,
|
img_cache,
|
||||||
&image,
|
&image,
|
||||||
ImageType::Content,
|
ImageType::Content,
|
||||||
cache_type.clone(),
|
cache_type,
|
||||||
|ui| {
|
|ui| {
|
||||||
ui.allocate_space(egui::vec2(spinsz, spinsz));
|
ui.allocate_space(egui::vec2(spinsz, spinsz));
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user