343 lines
9.2 KiB
Rust
343 lines
9.2 KiB
Rust
|
use crate::pagination::{Page, PageSource, Pagination};
|
||
|
use hyaenidae_profiles::store::{File, Profile, Submission};
|
||
|
use std::collections::HashMap;
|
||
|
use uuid::Uuid;
|
||
|
|
||
|
const PER_PAGE: usize = 12;
|
||
|
|
||
|
#[derive(Clone, Debug, Default)]
|
||
|
pub struct Cache {
|
||
|
pub(crate) submission_map: HashMap<Uuid, Submission>,
|
||
|
pub(crate) file_map: HashMap<Uuid, File>,
|
||
|
pub(crate) profile_map: HashMap<Uuid, Profile>,
|
||
|
pub(crate) blocks: HashMap<Uuid, bool>,
|
||
|
pub(crate) follows: HashMap<Uuid, bool>,
|
||
|
}
|
||
|
|
||
|
impl Cache {
|
||
|
pub(crate) fn new() -> Self {
|
||
|
Self::default()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub(crate) fn home_page(
|
||
|
viewer: Option<Uuid>,
|
||
|
store: &hyaenidae_profiles::State,
|
||
|
cache: &mut Cache,
|
||
|
source: Option<PageSource>,
|
||
|
) -> Page {
|
||
|
Page::from_pagination(
|
||
|
HomePager {
|
||
|
viewer,
|
||
|
store,
|
||
|
cache,
|
||
|
},
|
||
|
PER_PAGE,
|
||
|
source,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
pub(crate) fn draft_page(
|
||
|
viewer: Option<Uuid>,
|
||
|
profile_id: Uuid,
|
||
|
store: &hyaenidae_profiles::State,
|
||
|
cache: &mut Cache,
|
||
|
source: Option<PageSource>,
|
||
|
) -> Page {
|
||
|
Page::from_pagination(
|
||
|
DraftPager {
|
||
|
viewer,
|
||
|
profile_id,
|
||
|
store,
|
||
|
cache,
|
||
|
},
|
||
|
PER_PAGE,
|
||
|
source,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
pub(crate) fn main_page(
|
||
|
viewer: Option<Uuid>,
|
||
|
profile_id: Uuid,
|
||
|
store: &hyaenidae_profiles::State,
|
||
|
cache: &mut Cache,
|
||
|
source: Option<PageSource>,
|
||
|
) -> Page {
|
||
|
Page::from_pagination(
|
||
|
SubmissionPager {
|
||
|
viewer,
|
||
|
profile_id,
|
||
|
store,
|
||
|
cache,
|
||
|
},
|
||
|
PER_PAGE,
|
||
|
source,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
struct HomePager<'b> {
|
||
|
viewer: Option<Uuid>,
|
||
|
store: &'b hyaenidae_profiles::State,
|
||
|
cache: &'b mut Cache,
|
||
|
}
|
||
|
|
||
|
impl<'b> Pagination for HomePager<'b> {
|
||
|
fn from_max<'a>(&'a mut self, max: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.published_older_than(max)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn from_min<'a>(&'a mut self, min: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.published_newer_than(min)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn from_start<'a>(&'a mut self) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.published()
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct DraftPager<'b> {
|
||
|
viewer: Option<Uuid>,
|
||
|
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<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.drafted_older_than_for_profile(max)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn from_min<'a>(&'a mut self, min: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.drafted_newer_than_for_profile(min)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn from_start<'a>(&'a mut self) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.drafted_for_profile(self.profile_id)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct SubmissionPager<'b> {
|
||
|
viewer: Option<Uuid>,
|
||
|
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<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.published_older_than_for_profile(max)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn from_min<'a>(&'a mut self, min: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.published_newer_than_for_profile(min)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn from_start<'a>(&'a mut self) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||
|
Box::new(
|
||
|
self.store
|
||
|
.store
|
||
|
.submissions
|
||
|
.published_for_profile(self.profile_id)
|
||
|
.filter_map(filter_submissions(
|
||
|
self.viewer,
|
||
|
&self.store.store,
|
||
|
self.cache,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn filter_submissions<'a>(
|
||
|
viewer: Option<Uuid>,
|
||
|
store: &'a hyaenidae_profiles::store::Store,
|
||
|
cache: &'a mut Cache,
|
||
|
) -> impl FnMut(Uuid) -> Option<Uuid> + '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);
|
||
|
|
||
|
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)?;
|
||
|
|
||
|
Some(submission_id)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub(crate) fn can_view(
|
||
|
viewer: Option<Uuid>,
|
||
|
submission: &Submission,
|
||
|
store: &hyaenidae_profiles::store::Store,
|
||
|
cache: &mut Cache,
|
||
|
) -> Option<()> {
|
||
|
if let Some(viewer) = viewer {
|
||
|
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_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 {
|
||
|
let requires_login = if let Some(profile) = cache.profile_map.get(&submission.profile_id())
|
||
|
{
|
||
|
profile.login_required()
|
||
|
} else {
|
||
|
let profile = store.profiles.by_id(submission.profile_id()).ok()??;
|
||
|
let requires_login = profile.login_required();
|
||
|
cache.profile_map.insert(profile.id(), profile);
|
||
|
requires_login
|
||
|
};
|
||
|
|
||
|
if requires_login {
|
||
|
return None;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Some(())
|
||
|
}
|