408 lines
11 KiB
Rust
408 lines
11 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 = 24;
|
|
|
|
#[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 browse_page(
|
|
viewer: Option<Uuid>,
|
|
can_view_sensitive: bool,
|
|
store: &hyaenidae_profiles::State,
|
|
cache: &mut Cache,
|
|
source: Option<PageSource>,
|
|
) -> Page {
|
|
Page::from_pagination(
|
|
BrowsePager {
|
|
can_view_sensitive,
|
|
viewer,
|
|
store,
|
|
cache,
|
|
},
|
|
PER_PAGE,
|
|
source,
|
|
)
|
|
}
|
|
|
|
pub(crate) fn draft_page(
|
|
viewer: Option<Uuid>,
|
|
can_view_sensitive: bool,
|
|
profile_id: Uuid,
|
|
store: &hyaenidae_profiles::State,
|
|
cache: &mut Cache,
|
|
source: Option<PageSource>,
|
|
) -> Page {
|
|
Page::from_pagination(
|
|
DraftPager {
|
|
can_view_sensitive,
|
|
viewer,
|
|
profile_id,
|
|
store,
|
|
cache,
|
|
},
|
|
PER_PAGE,
|
|
source,
|
|
)
|
|
}
|
|
|
|
pub(crate) fn main_page(
|
|
viewer: Option<Uuid>,
|
|
can_view_sensitive: bool,
|
|
profile_id: Uuid,
|
|
store: &hyaenidae_profiles::State,
|
|
cache: &mut Cache,
|
|
source: Option<PageSource>,
|
|
) -> 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<Uuid>,
|
|
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<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,
|
|
false,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
|
|
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,
|
|
false,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
|
|
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,
|
|
false,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
}
|
|
|
|
struct DraftPager<'b> {
|
|
can_view_sensitive: bool,
|
|
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,
|
|
true,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
|
|
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,
|
|
true,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
|
|
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,
|
|
true,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
}
|
|
|
|
struct SubmissionPager<'b> {
|
|
can_view_sensitive: bool,
|
|
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,
|
|
true,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
|
|
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,
|
|
true,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
|
|
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,
|
|
true,
|
|
self.can_view_sensitive,
|
|
)),
|
|
)
|
|
}
|
|
}
|
|
|
|
fn filter_submissions<'a>(
|
|
viewer: Option<Uuid>,
|
|
store: &'a hyaenidae_profiles::store::Store,
|
|
cache: &'a mut Cache,
|
|
show_unlisted: bool,
|
|
can_view_sensitive: bool,
|
|
) -> 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,
|
|
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<Uuid>,
|
|
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(())
|
|
}
|