hyaenidae/src/pagination/submission.rs
asonix 010dd2952f Server: Expose NSFW toggle, Dark Mode toggle
Ensure all submission view permission logic is the same
2021-02-03 21:09:25 -06:00

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(())
}