diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs index a7a69b7..6ea1cbc 100644 --- a/crates/notedeck_columns/src/app.rs +++ b/crates/notedeck_columns/src/app.rs @@ -13,7 +13,7 @@ use crate::{ }; use notedeck::{Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState, UnknownIds}; -use notedeck_ui::NoteOptions; +use notedeck_ui::{jobs::JobsCache, NoteOptions}; use enostr::{ClientMessage, Keypair, PoolRelay, Pubkey, RelayEvent, RelayMessage, RelayPool}; use uuid::Uuid; @@ -42,6 +42,7 @@ pub struct Damus { pub timeline_cache: TimelineCache, pub subscriptions: Subscriptions, pub support: Support, + pub jobs: JobsCache, //frame_history: crate::frame_history::FrameHistory, @@ -430,6 +431,8 @@ impl Damus { note_options.set_scramble_text(parsed_args.scramble); note_options.set_hide_media(parsed_args.no_media); + let jobs = JobsCache::default(); + Self { subscriptions: Subscriptions::default(), since_optimize: parsed_args.since_optimize, @@ -444,6 +447,7 @@ impl Damus { decks_cache, debug, unrecognized_args, + jobs, } } @@ -487,6 +491,7 @@ impl Damus { support, decks_cache, unrecognized_args: BTreeSet::default(), + jobs: JobsCache::default(), } } diff --git a/crates/notedeck_ui/src/jobs.rs b/crates/notedeck_ui/src/jobs.rs new file mode 100644 index 0000000..32c9913 --- /dev/null +++ b/crates/notedeck_ui/src/jobs.rs @@ -0,0 +1,153 @@ +use egui::TextureHandle; +use hashbrown::{hash_map::RawEntryMut, HashMap}; +use notedeck::JobPool; +use poll_promise::Promise; + +#[derive(Default)] +pub struct JobsCache { + jobs: HashMap, +} + +pub enum JobState { + Pending(Promise>>), + Error(JobError), + Completed(Job), +} + +pub enum JobError { + InvalidParameters, +} + +#[derive(Debug)] +pub enum JobParams<'a> { + Blurhash(BlurhashParams<'a>), +} + +#[derive(Debug)] +pub enum JobParamsOwned { + Blurhash(BlurhashParamsOwned), +} + +impl<'a> From> for BlurhashParamsOwned { + fn from(params: BlurhashParams<'a>) -> Self { + BlurhashParamsOwned { + blurhash: params.blurhash.to_owned(), + url: params.url.to_owned(), + ctx: params.ctx.clone(), + } + } +} + +impl<'a> From> for JobParamsOwned { + fn from(params: JobParams<'a>) -> Self { + match params { + JobParams::Blurhash(bp) => JobParamsOwned::Blurhash(bp.into()), + } + } +} + +#[derive(Debug)] +pub struct BlurhashParams<'a> { + pub blurhash: &'a str, + pub url: &'a str, + pub ctx: &'a egui::Context, +} + +#[derive(Debug)] +pub struct BlurhashParamsOwned { + pub blurhash: String, + pub url: String, + pub ctx: egui::Context, +} + +impl JobsCache { + pub fn get_or_insert_with< + 'a, + F: FnOnce(Option) -> Result + Send + 'static, + >( + &'a mut self, + job_pool: &mut JobPool, + jobid: &JobId, + params: Option, + run_job: F, + ) -> &'a mut JobState { + match self.jobs.raw_entry_mut().from_key(jobid) { + RawEntryMut::Occupied(entry) => 's: { + let mut state = entry.into_mut(); + + let JobState::Pending(promise) = &mut state else { + break 's state; + }; + + let Some(res) = promise.ready_mut() else { + break 's state; + }; + + let Some(res) = res.take() else { + tracing::error!("Failed to take the promise for job: {:?}", jobid); + break 's state; + }; + + *state = match res { + Ok(j) => JobState::Completed(j), + Err(e) => JobState::Error(e), + }; + + state + } + RawEntryMut::Vacant(entry) => { + let owned_params = params.map(JobParams::into); + let wrapped: Box Option> + Send + 'static> = + Box::new(move || Some(run_job(owned_params))); + + let promise = Promise::spawn_async(job_pool.schedule(wrapped)); + + let (_, state) = entry.insert(jobid.into(), JobState::Pending(promise)); + + state + } + } + } + + pub fn get(&self, jobid: &JobId) -> Option<&JobState> { + self.jobs.get(jobid) + } +} + +impl<'a> From<&JobId<'a>> for JobIdOwned { + fn from(jobid: &JobId<'a>) -> Self { + match jobid { + JobId::Blurhash(s) => JobIdOwned::Blurhash(s.to_string()), + } + } +} + +impl hashbrown::Equivalent for JobId<'_> { + fn equivalent(&self, key: &JobIdOwned) -> bool { + match (self, key) { + (JobId::Blurhash(a), JobIdOwned::Blurhash(b)) => *a == b.as_str(), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +enum JobIdOwned { + Blurhash(String), // image URL +} + +#[derive(Debug, Hash)] +pub enum JobId<'a> { + Blurhash(&'a str), // image URL +} + +pub enum Job { + Blurhash(Option), +} + +impl std::fmt::Debug for Job { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Job::Blurhash(_) => write!(f, "Blurhash"), + } + } +} diff --git a/crates/notedeck_ui/src/lib.rs b/crates/notedeck_ui/src/lib.rs index 94f8a93..e2b6a54 100644 --- a/crates/notedeck_ui/src/lib.rs +++ b/crates/notedeck_ui/src/lib.rs @@ -5,6 +5,7 @@ pub mod contacts; pub mod gif; pub mod icons; pub mod images; +pub mod jobs; pub mod mention; pub mod note; pub mod profile;