mirror of
https://github.com/aljazceru/notedeck.git
synced 2025-12-18 00:54:21 +01:00
@@ -13,18 +13,183 @@ use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
use hex::ToHex;
|
||||
use sha2::Digest;
|
||||
use std::path;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{self};
|
||||
use tracing::warn;
|
||||
|
||||
pub type MediaCacheValue = Promise<Result<TexturedImage>>;
|
||||
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 {
|
||||
Static(TextureHandle),
|
||||
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 first_frame: TextureFrame,
|
||||
pub other_frames: Vec<TextureFrame>,
|
||||
@@ -60,7 +225,7 @@ pub struct MediaCache {
|
||||
url_imgs: MediaCacheMap,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
pub enum MediaCacheType {
|
||||
Image,
|
||||
Gif,
|
||||
@@ -215,6 +380,20 @@ impl Images {
|
||||
self.static_imgs.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>;
|
||||
|
||||
@@ -41,8 +41,8 @@ pub use error::{Error, FilterError, ZapError};
|
||||
pub use filter::{FilterState, FilterStates, UnifiedSubscription};
|
||||
pub use fonts::NamedFontFamily;
|
||||
pub use imgcache::{
|
||||
Animation, GifState, GifStateMap, ImageFrame, Images, MediaCache, MediaCacheType,
|
||||
MediaCacheValue, TextureFrame, TexturedImage,
|
||||
Animation, GifState, GifStateMap, ImageFrame, Images, LoadableTextureState, MediaCache,
|
||||
MediaCacheType, TextureFrame, TextureState, TexturedImage, TexturesCache,
|
||||
};
|
||||
pub use job_pool::JobPool;
|
||||
pub use muted::{MuteFun, Muted};
|
||||
|
||||
@@ -468,7 +468,7 @@ fn render_media_cache(
|
||||
let m_cached_promise = cache.map().get(url);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ fn image_carousel(
|
||||
ui.ctx().memory(|mem| {
|
||||
mem.data
|
||||
.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,
|
||||
&image,
|
||||
ImageType::Content,
|
||||
cache_type.clone(),
|
||||
cache_type,
|
||||
|ui| {
|
||||
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("current_image"),
|
||||
(image.clone(), cache_type.clone()),
|
||||
(image.clone(), cache_type),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -438,7 +438,7 @@ fn image_carousel(
|
||||
img_cache,
|
||||
&image,
|
||||
ImageType::Content,
|
||||
cache_type.clone(),
|
||||
cache_type,
|
||||
|ui| {
|
||||
ui.allocate_space(egui::vec2(spinsz, spinsz));
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user