use crate::pagination::{Page, PageSource, Pagination}; use hyaenidae_profiles::store::{File, Profile, Submission}; use std::collections::HashMap; use uuid::Uuid; const PER_PAGE: usize = 24; #[derive(Clone, Debug, Default)] pub struct Cache { pub(crate) submission_map: HashMap, pub(crate) file_map: HashMap, pub(crate) profile_map: HashMap, pub(crate) blocks: HashMap, pub(crate) follows: HashMap, } impl Cache { pub(crate) fn new() -> Self { Self::default() } } pub(crate) fn browse_page( viewer: Option, can_view_sensitive: bool, store: &hyaenidae_profiles::State, cache: &mut Cache, source: Option, ) -> Page { Page::from_pagination( BrowsePager { can_view_sensitive, viewer, store, cache, }, PER_PAGE, source, ) } pub(crate) fn draft_page( viewer: Option, can_view_sensitive: bool, profile_id: Uuid, store: &hyaenidae_profiles::State, cache: &mut Cache, source: Option, ) -> Page { Page::from_pagination( DraftPager { can_view_sensitive, viewer, profile_id, store, cache, }, PER_PAGE, source, ) } pub(crate) fn main_page( viewer: Option, can_view_sensitive: bool, profile_id: Uuid, store: &hyaenidae_profiles::State, cache: &mut Cache, source: Option, ) -> Page { Page::from_pagination( SubmissionPager { can_view_sensitive, viewer, profile_id, store, cache, }, PER_PAGE, source, ) } struct BrowsePager<'b> { can_view_sensitive: bool, viewer: Option, store: &'b hyaenidae_profiles::State, cache: &'b mut Cache, } impl<'b> Pagination for BrowsePager<'b> { fn from_max<'a>(&'a mut self, max: Uuid) -> Box + 'a> { Box::new( self.store .store .submissions .published_older_than(max) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, false, self.can_view_sensitive, )), ) } fn from_min<'a>(&'a mut self, min: Uuid) -> Box + 'a> { Box::new( self.store .store .submissions .published_newer_than(min) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, false, self.can_view_sensitive, )), ) } fn from_start<'a>(&'a mut self) -> Box + 'a> { Box::new( self.store .store .submissions .published() .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, false, self.can_view_sensitive, )), ) } } struct DraftPager<'b> { can_view_sensitive: bool, viewer: Option, profile_id: Uuid, store: &'b hyaenidae_profiles::State, cache: &'b mut Cache, } impl<'b> Pagination for DraftPager<'b> { fn from_max<'a>(&'a mut self, max: Uuid) -> Box + 'a> { Box::new( self.store .store .submissions .drafted_older_than_for_profile(max) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, true, self.can_view_sensitive, )), ) } fn from_min<'a>(&'a mut self, min: Uuid) -> Box + 'a> { Box::new( self.store .store .submissions .drafted_newer_than_for_profile(min) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, true, self.can_view_sensitive, )), ) } fn from_start<'a>(&'a mut self) -> Box + 'a> { Box::new( self.store .store .submissions .drafted_for_profile(self.profile_id) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, true, self.can_view_sensitive, )), ) } } struct SubmissionPager<'b> { can_view_sensitive: bool, viewer: Option, profile_id: Uuid, store: &'b hyaenidae_profiles::State, cache: &'b mut Cache, } impl<'b> Pagination for SubmissionPager<'b> { fn from_max<'a>(&'a mut self, max: Uuid) -> Box + 'a> { Box::new( self.store .store .submissions .published_older_than_for_profile(max) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, true, self.can_view_sensitive, )), ) } fn from_min<'a>(&'a mut self, min: Uuid) -> Box + 'a> { Box::new( self.store .store .submissions .published_newer_than_for_profile(min) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, true, self.can_view_sensitive, )), ) } fn from_start<'a>(&'a mut self) -> Box + 'a> { Box::new( self.store .store .submissions .published_for_profile(self.profile_id) .filter_map(filter_submissions( self.viewer, &self.store.store, self.cache, true, self.can_view_sensitive, )), ) } } fn filter_submissions<'a>( viewer: Option, store: &'a hyaenidae_profiles::store::Store, cache: &'a mut Cache, show_unlisted: bool, can_view_sensitive: bool, ) -> impl FnMut(Uuid) -> Option + 'a { move |submission_id| { if !cache.submission_map.contains_key(&submission_id) { let submission = store.submissions.by_id(submission_id).ok()??; if !cache.profile_map.contains_key(&submission.profile_id()) { let profile = store.profiles.by_id(submission.profile_id()).ok()??; cache.profile_map.insert(profile.id(), profile); } let opt = can_view( viewer, &submission, store, cache, show_unlisted, can_view_sensitive, ); if let Some(file_id) = submission.files().get(0) { if !cache.file_map.contains_key(file_id) { let file = store.files.by_id(*file_id).ok()??; cache.file_map.insert(file.id(), file); } } cache.submission_map.insert(submission.id(), submission); opt.map(|_| submission_id) } else { let submission = cache.submission_map.get(&submission_id)?.clone(); can_view( viewer, &submission, store, cache, show_unlisted, can_view_sensitive, )?; Some(submission_id) } } } pub(crate) fn can_view( viewer: Option, submission: &Submission, store: &hyaenidae_profiles::store::Store, cache: &mut Cache, show_unlisted: bool, can_view_sensitive: bool, ) -> Option<()> { if let Some(viewer) = viewer { if viewer == submission.profile_id() { return Some(()); } if submission.is_sensitive() && !can_view_sensitive { return None; } if let Some(block) = cache.blocks.get(&submission.profile_id()) { if *block { return None; } } else { let is_blocked = store .view .blocks .by_forward(viewer, submission.profile_id()) .ok()? .is_some(); cache.blocks.insert(submission.profile_id(), is_blocked); if is_blocked { return None; } let is_blocking = store .view .blocks .by_forward(submission.profile_id(), viewer) .ok()? .is_some(); cache.blocks.insert(submission.profile_id(), is_blocking); if is_blocking { return None; } } if (submission.is_unlisted() && !show_unlisted) || submission.is_followers_only() { if let Some(follow) = cache.follows.get(&submission.profile_id()) { if !*follow { return None; } } else { let is_following = store .view .follows .by_forward(submission.profile_id(), viewer) .ok()? .is_some(); cache.follows.insert(submission.profile_id(), is_following); if !is_following { return None; } } } } else { if submission.is_sensitive() { return None; } if submission.is_unlisted() && !show_unlisted { return None; } if submission.is_followers_only() { return None; } let requires_login = submission.is_logged_in_only() || if let Some(profile) = cache.profile_map.get(&submission.profile_id()) { profile.local_owner().is_none() || profile.login_required() } else { let profile = store.profiles.by_id(submission.profile_id()).ok()??; let requires_login = profile.local_owner().is_none() || profile.login_required(); cache.profile_map.insert(profile.id(), profile); requires_login }; if requires_login { return None; } } Some(()) }