Server: Add admin reports, server pagination
This commit is contained in:
parent
c1ac9eeed4
commit
555aba8d36
221
src/admin/mod.rs
221
src/admin/mod.rs
|
@ -2,7 +2,7 @@ use crate::{
|
|||
error::{Error, OptionExt},
|
||||
extensions::{CommentExt, ProfileExt, SubmissionExt},
|
||||
nav::NavState,
|
||||
pagination::{PageNum, SearchPage},
|
||||
pagination::{Page, PageNum, PageSource, SearchPage},
|
||||
views::{OwnedProfileView, OwnedSubmissionView},
|
||||
ActixLoader, State,
|
||||
};
|
||||
|
@ -23,7 +23,8 @@ pub use hyaenidae_profiles::store::Report;
|
|||
|
||||
mod pagination;
|
||||
use pagination::{
|
||||
BlockedPager, FederatedPager, InboundPager, KnownPager, OutboundPager, ServerPager,
|
||||
BlockedPager, ClosedPager, FederatedPager, InboundPager, KnownPager, OpenPager, OutboundPager,
|
||||
ReportPager, ServerPager,
|
||||
};
|
||||
|
||||
pub(super) fn scope() -> Scope {
|
||||
|
@ -123,20 +124,21 @@ async fn discover_server(
|
|||
Ok(()) as Result<(), Error>
|
||||
};
|
||||
|
||||
let query = query.into_inner();
|
||||
if let Err(e) = (fallible)().await {
|
||||
let mut federation_view = FederationView::build(query.into_inner(), &state2).await?;
|
||||
let mut federation_view = FederationView::build(query.clone(), &state2).await?;
|
||||
federation_view
|
||||
.discover_error(e.to_string())
|
||||
.discover_value(url2);
|
||||
|
||||
let server_view = ServerView::build(&state2).await?;
|
||||
let open_reports = ReportsView::new(state2).await?;
|
||||
let reports_vew = ReportsView::new(query, state2).await?;
|
||||
|
||||
return crate::rendered(HttpResponse::Ok(), |cursor| {
|
||||
crate::templates::admin::index(
|
||||
cursor,
|
||||
&loader,
|
||||
&open_reports,
|
||||
&reports_vew,
|
||||
&server_view,
|
||||
&federation_view,
|
||||
&nav_state,
|
||||
|
@ -878,6 +880,17 @@ impl FederationView {
|
|||
}
|
||||
}
|
||||
|
||||
fn page_source(query: &HashMap<String, String>, prefix: &str) -> Option<PageSource> {
|
||||
query
|
||||
.get(&format!("{}_min", prefix))
|
||||
.and_then(|min_str| Some(PageSource::NewerThan(min_str.parse().ok()?)))
|
||||
.or_else(|| {
|
||||
query
|
||||
.get(&format!("{}_max", prefix))
|
||||
.and_then(|max_str| Some(PageSource::OlderThan(max_str.parse().ok()?)))
|
||||
})
|
||||
}
|
||||
|
||||
fn page_num(query: &HashMap<String, String>, name: &str) -> Option<PageNum> {
|
||||
query.get(name).and_then(|page_str| {
|
||||
Some(PageNum {
|
||||
|
@ -923,7 +936,7 @@ async fn view_report(
|
|||
) -> Result<HttpResponse, Error> {
|
||||
let report_id = report.into_inner();
|
||||
let report_store = state.profiles.store.reports.clone();
|
||||
let report = web::block(move || Ok(report_store.by_id(report_id)?)).await?;
|
||||
let report = web::block(move || report_store.by_id(report_id)?.req()).await?;
|
||||
|
||||
let report_view = ReportView::new(report, state).await?;
|
||||
|
||||
|
@ -966,7 +979,7 @@ async fn close_report(
|
|||
) -> Result<HttpResponse, Error> {
|
||||
let report_id = report.into_inner();
|
||||
let report_store = state.profiles.store.reports.clone();
|
||||
let report = web::block(move || Ok(report_store.by_id(report_id)?)).await?;
|
||||
let report = web::block(move || report_store.by_id(report_id)?.req()).await?;
|
||||
|
||||
let form = form.into_inner();
|
||||
|
||||
|
@ -1110,15 +1123,16 @@ async fn admin_page(
|
|||
nav_state: NavState,
|
||||
state: web::Data<State>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let federation_view = FederationView::build(query.into_inner(), &state).await?;
|
||||
let query = query.into_inner();
|
||||
let federation_view = FederationView::build(query.clone(), &state).await?;
|
||||
let server_view = ServerView::build(&state).await?;
|
||||
let open_reports = ReportsView::new(state).await?;
|
||||
let reports_vew = ReportsView::new(query, state).await?;
|
||||
|
||||
crate::rendered(HttpResponse::Ok(), |cursor| {
|
||||
crate::templates::admin::index(
|
||||
cursor,
|
||||
&loader,
|
||||
&open_reports,
|
||||
&reports_vew,
|
||||
&server_view,
|
||||
&federation_view,
|
||||
&nav_state,
|
||||
|
@ -1417,7 +1431,10 @@ impl ServerView {
|
|||
}
|
||||
|
||||
pub struct ReportsView {
|
||||
reports: Vec<Report>,
|
||||
query: HashMap<String, String>,
|
||||
open_reports: Page,
|
||||
closed_reports: Page,
|
||||
reports: HashMap<Uuid, Report>,
|
||||
profiles: HashMap<Uuid, Profile>,
|
||||
submissions: HashMap<Uuid, Submission>,
|
||||
comments: HashMap<Uuid, Comment>,
|
||||
|
@ -1511,8 +1528,94 @@ impl<'a> SubmissionView<'a> {
|
|||
}
|
||||
|
||||
impl ReportsView {
|
||||
pub(crate) fn reports(&self) -> &[Report] {
|
||||
&self.reports
|
||||
pub(crate) fn open_reports<'a>(&'a self) -> impl Iterator<Item = &'a Report> + 'a {
|
||||
self.open_reports
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(move |report_id| self.reports.get(report_id))
|
||||
}
|
||||
|
||||
pub(crate) fn has_open_reports_nav(&self) -> bool {
|
||||
self.open_reports.prev.is_some() || self.open_reports.next.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn open_reports_nav(&self, loader: &ActixLoader) -> Vec<Button> {
|
||||
let mut btns = vec![];
|
||||
|
||||
if let Some(prev) = self.open_reports.prev {
|
||||
let mut query = self.query.clone();
|
||||
query.insert("open_min".to_owned(), prev.to_string());
|
||||
query.remove("open_max");
|
||||
|
||||
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||
format!("/admin?{}", query)
|
||||
} else {
|
||||
"/admin".to_owned()
|
||||
};
|
||||
|
||||
btns.push(Button::secondary(&fl!(loader, "admin-reports-prev")).href(&href));
|
||||
}
|
||||
|
||||
if let Some(next) = self.open_reports.next {
|
||||
let mut query = self.query.clone();
|
||||
query.insert("open_max".to_owned(), next.to_string());
|
||||
query.remove("open_min");
|
||||
|
||||
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||
format!("/admin?{}", query)
|
||||
} else {
|
||||
"/admin".to_owned()
|
||||
};
|
||||
|
||||
btns.push(Button::secondary(&fl!(loader, "admin-reports-next")).href(&href));
|
||||
}
|
||||
|
||||
btns
|
||||
}
|
||||
|
||||
pub(crate) fn closed_reports<'a>(&'a self) -> impl Iterator<Item = &'a Report> + 'a {
|
||||
self.closed_reports
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(move |report_id| self.reports.get(report_id))
|
||||
}
|
||||
|
||||
pub(crate) fn has_closed_reports_nav(&self) -> bool {
|
||||
self.closed_reports.prev.is_some() || self.closed_reports.next.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn closed_reports_nav(&self, loader: &ActixLoader) -> Vec<Button> {
|
||||
let mut btns = vec![];
|
||||
|
||||
if let Some(prev) = self.closed_reports.prev {
|
||||
let mut query = self.query.clone();
|
||||
query.insert("closed_min".to_owned(), prev.to_string());
|
||||
query.remove("closed_max");
|
||||
|
||||
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||
format!("/admin?{}", query)
|
||||
} else {
|
||||
"/admin".to_owned()
|
||||
};
|
||||
|
||||
btns.push(Button::secondary(&fl!(loader, "admin-reports-prev")).href(&href));
|
||||
}
|
||||
|
||||
if let Some(next) = self.closed_reports.next {
|
||||
let mut query = self.query.clone();
|
||||
query.insert("closed_max".to_owned(), next.to_string());
|
||||
query.remove("closed_min");
|
||||
|
||||
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||
format!("/admin?{}", query)
|
||||
} else {
|
||||
"/admin".to_owned()
|
||||
};
|
||||
|
||||
btns.push(Button::secondary(&fl!(loader, "admin-reports-next")).href(&href));
|
||||
}
|
||||
|
||||
btns
|
||||
}
|
||||
|
||||
pub(crate) fn view_path(&self, report: &Report) -> String {
|
||||
|
@ -1555,80 +1658,46 @@ impl ReportsView {
|
|||
})
|
||||
}
|
||||
|
||||
async fn new(state: web::Data<State>) -> Result<Self, Error> {
|
||||
async fn new(query: HashMap<String, String>, state: web::Data<State>) -> Result<Self, Error> {
|
||||
let store = state.profiles.clone();
|
||||
|
||||
let view = web::block(move || {
|
||||
let mut reports = HashMap::new();
|
||||
let mut profiles = HashMap::new();
|
||||
let mut submissions = HashMap::new();
|
||||
let mut comments = HashMap::new();
|
||||
let mut files = HashMap::new();
|
||||
|
||||
let reports = store
|
||||
.store
|
||||
.reports
|
||||
.all()
|
||||
.filter_map(|id| {
|
||||
let report = store.store.reports.by_id(id).ok()?;
|
||||
let open_reports = Page::from_pagination(
|
||||
OpenPager(ReportPager {
|
||||
store: &store.store,
|
||||
reports: &mut reports,
|
||||
profiles: &mut profiles,
|
||||
submissions: &mut submissions,
|
||||
comments: &mut comments,
|
||||
files: &mut files,
|
||||
}),
|
||||
10,
|
||||
page_source(&query, "open"),
|
||||
);
|
||||
|
||||
if let Some(id) = report.reporter_profile() {
|
||||
if !profiles.contains_key(&id) {
|
||||
let profile = store.store.profiles.by_id(id).ok()??;
|
||||
profiles.insert(profile.id(), profile);
|
||||
}
|
||||
}
|
||||
|
||||
match report.kind() {
|
||||
ReportKind::Profile => {
|
||||
if !profiles.contains_key(&report.item()) {
|
||||
let profile = store.store.profiles.by_id(report.item()).ok()??;
|
||||
profiles.insert(profile.id(), profile);
|
||||
}
|
||||
}
|
||||
ReportKind::Submission => {
|
||||
if !submissions.contains_key(&report.item()) {
|
||||
let submission =
|
||||
store.store.submissions.by_id(report.item()).ok()??;
|
||||
|
||||
if !profiles.contains_key(&submission.profile_id()) {
|
||||
let profile = store
|
||||
.store
|
||||
.profiles
|
||||
.by_id(submission.profile_id())
|
||||
.ok()??;
|
||||
profiles.insert(profile.id(), profile);
|
||||
}
|
||||
|
||||
for file_id in submission.files() {
|
||||
if !files.contains_key(file_id) {
|
||||
let file = store.store.files.by_id(*file_id).ok()??;
|
||||
files.insert(file.id(), file);
|
||||
}
|
||||
}
|
||||
|
||||
submissions.insert(submission.id(), submission);
|
||||
}
|
||||
}
|
||||
ReportKind::Comment => {
|
||||
if !comments.contains_key(&report.item()) {
|
||||
let comment = store.store.comments.by_id(report.item()).ok()??;
|
||||
if !profiles.contains_key(&comment.profile_id()) {
|
||||
let profile =
|
||||
store.store.profiles.by_id(comment.profile_id()).ok()??;
|
||||
profiles.insert(profile.id(), profile);
|
||||
}
|
||||
comments.insert(comment.id(), comment);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Some(report)
|
||||
})
|
||||
.rev()
|
||||
.collect();
|
||||
let closed_reports = Page::from_pagination(
|
||||
ClosedPager(ReportPager {
|
||||
store: &store.store,
|
||||
reports: &mut reports,
|
||||
profiles: &mut profiles,
|
||||
submissions: &mut submissions,
|
||||
comments: &mut comments,
|
||||
files: &mut files,
|
||||
}),
|
||||
10,
|
||||
page_source(&query, "closed"),
|
||||
);
|
||||
|
||||
Ok(ReportsView {
|
||||
query,
|
||||
open_reports,
|
||||
closed_reports,
|
||||
reports,
|
||||
profiles,
|
||||
submissions,
|
||||
|
|
|
@ -1,8 +1,20 @@
|
|||
use crate::pagination::SearchPagination;
|
||||
use hyaenidae_profiles::store::Server;
|
||||
use crate::pagination::{Pagination, SearchPagination};
|
||||
use hyaenidae_profiles::store::{Comment, File, Profile, Report, ReportKind, Server, Submission};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub(super) struct ReportPager<'b> {
|
||||
pub(super) store: &'b hyaenidae_profiles::store::Store,
|
||||
pub(super) reports: &'b mut HashMap<Uuid, Report>,
|
||||
pub(super) profiles: &'b mut HashMap<Uuid, Profile>,
|
||||
pub(super) submissions: &'b mut HashMap<Uuid, Submission>,
|
||||
pub(super) comments: &'b mut HashMap<Uuid, Comment>,
|
||||
pub(super) files: &'b mut HashMap<Uuid, File>,
|
||||
}
|
||||
|
||||
pub(super) struct OpenPager<'b>(pub(super) ReportPager<'b>);
|
||||
pub(super) struct ClosedPager<'b>(pub(super) ReportPager<'b>);
|
||||
|
||||
pub(super) struct ServerPager<'b> {
|
||||
pub(super) self_id: Uuid,
|
||||
pub(super) store: &'b hyaenidae_profiles::store::Store,
|
||||
|
@ -15,6 +27,153 @@ pub(super) struct OutboundPager<'a>(pub(super) ServerPager<'a>);
|
|||
pub(super) struct BlockedPager<'a>(pub(super) ServerPager<'a>);
|
||||
pub(super) struct KnownPager<'a>(pub(super) ServerPager<'a>);
|
||||
|
||||
impl<'b> Pagination for OpenPager<'b> {
|
||||
fn from_max<'a>(&'a mut self, max: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||
Box::new(
|
||||
self.0
|
||||
.store
|
||||
.reports
|
||||
.open_reports_older_than(max)
|
||||
.filter_map(move |report_id| self.0.filter_report(report_id)),
|
||||
)
|
||||
}
|
||||
|
||||
fn from_min<'a>(&'a mut self, min: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||
Box::new(
|
||||
self.0
|
||||
.store
|
||||
.reports
|
||||
.open_reports_newer_than(min)
|
||||
.filter_map(move |report_id| self.0.filter_report(report_id)),
|
||||
)
|
||||
}
|
||||
|
||||
fn from_start<'a>(&'a mut self) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||
Box::new(
|
||||
self.0
|
||||
.store
|
||||
.reports
|
||||
.open_reports()
|
||||
.filter_map(move |report_id| self.0.filter_report(report_id)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Pagination for ClosedPager<'b> {
|
||||
fn from_max<'a>(&'a mut self, max: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||
Box::new(
|
||||
self.0
|
||||
.store
|
||||
.reports
|
||||
.closed_reports_older_than(max)
|
||||
.filter_map(move |report_id| self.0.filter_report(report_id)),
|
||||
)
|
||||
}
|
||||
|
||||
fn from_min<'a>(&'a mut self, min: Uuid) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||
Box::new(
|
||||
self.0
|
||||
.store
|
||||
.reports
|
||||
.closed_reports_newer_than(min)
|
||||
.filter_map(move |report_id| self.0.filter_report(report_id)),
|
||||
)
|
||||
}
|
||||
|
||||
fn from_start<'a>(&'a mut self) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||
Box::new(
|
||||
self.0
|
||||
.store
|
||||
.reports
|
||||
.closed_reports()
|
||||
.filter_map(move |report_id| self.0.filter_report(report_id)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> ReportPager<'b> {
|
||||
fn filter_report(&mut self, report_id: Uuid) -> Option<Uuid> {
|
||||
if !self.reports.contains_key(&report_id) {
|
||||
let report = self.store.reports.by_id(report_id).ok()??;
|
||||
|
||||
if let Some(id) = report.reporter_profile() {
|
||||
self.cache_profile(id)?;
|
||||
}
|
||||
|
||||
match report.kind() {
|
||||
ReportKind::Profile => {
|
||||
self.cache_profile(report.item())?;
|
||||
}
|
||||
ReportKind::Submission => {
|
||||
self.cache_submission(report.item())?;
|
||||
}
|
||||
ReportKind::Comment => {
|
||||
self.cache_comment(report.item())?;
|
||||
}
|
||||
ReportKind::Post => {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
self.reports.insert(report.id(), report);
|
||||
|
||||
Some(report_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn cache_comment(&mut self, comment_id: Uuid) -> Option<()> {
|
||||
if !self.comments.contains_key(&comment_id) {
|
||||
let comment = self.store.comments.by_id(comment_id).ok()??;
|
||||
|
||||
self.cache_profile(comment.profile_id())?;
|
||||
self.comments.insert(comment.id(), comment);
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn cache_submission(&mut self, submission_id: Uuid) -> Option<()> {
|
||||
if !self.submissions.contains_key(&submission_id) {
|
||||
let submission = self.store.submissions.by_id(submission_id).ok()??;
|
||||
|
||||
self.cache_profile(submission.profile_id())?;
|
||||
|
||||
for file_id in submission.files() {
|
||||
self.cache_file(*file_id)?;
|
||||
}
|
||||
|
||||
self.submissions.insert(submission.id(), submission);
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn cache_profile(&mut self, profile_id: Uuid) -> Option<()> {
|
||||
if !self.profiles.contains_key(&profile_id) {
|
||||
let profile = self.store.profiles.by_id(profile_id).ok()??;
|
||||
|
||||
for file_id in profile.icon().into_iter().chain(profile.banner()) {
|
||||
self.cache_file(file_id)?;
|
||||
}
|
||||
|
||||
self.profiles.insert(profile.id(), profile);
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn cache_file(&mut self, file_id: Uuid) -> Option<()> {
|
||||
if !self.files.contains_key(&file_id) {
|
||||
let file = self.store.files.by_id(file_id).ok()??;
|
||||
self.files.insert(file.id(), file);
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> SearchPagination for FederatedPager<'b> {
|
||||
fn from_term<'a>(&'a mut self, _: &'a str) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||
Box::new(
|
||||
|
|
|
@ -154,6 +154,10 @@ impl NavState {
|
|||
"/notifications"
|
||||
}
|
||||
|
||||
pub(crate) fn is_admin(&self) -> bool {
|
||||
self.admin.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn admin_button(&self, loader: &ActixLoader) -> Button {
|
||||
Button::link(&fl!(loader, "nav-admin-button")).href("/admin")
|
||||
}
|
||||
|
|
|
@ -36,53 +36,55 @@
|
|||
@:card_title({
|
||||
@fl!(loader, "admin-reports-heading")
|
||||
})
|
||||
@for report in reports_view.reports() {
|
||||
@for report in reports_view.open_reports() {
|
||||
@:card_body({
|
||||
<div class="report">
|
||||
<div class="report-head">
|
||||
@if let Some(profile) = reports_view.profile(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
@fl!(loader, "admin-reports-reported")
|
||||
<div class="report-left">
|
||||
<div class="report-head">
|
||||
@if let Some(profile) = reports_view.profile(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
@fl!(loader, "admin-reports-reported")
|
||||
|
||||
@:link(&Link::new_tab(&profile.view_path()).plain(true), {
|
||||
@Html(profile.name())
|
||||
@:link(&Link::new_tab(&profile.view_path()).plain(true), {
|
||||
@Html(profile.name())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@if let Some(submission) = reports_view.submission(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
@fl!(loader, "admin-reports-reported")
|
||||
}
|
||||
@if let Some(submission) = reports_view.submission(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
@fl!(loader, "admin-reports-reported")
|
||||
|
||||
@:link(&Link::new_tab(&submission.author_path()).plain(true), {
|
||||
@Html(fl!(loader, "author-owned", author = submission.author_name()))
|
||||
})
|
||||
@fl!(loader, "admin-reports-submission")
|
||||
@:link(&Link::new_tab(&submission.author_path()).plain(true), {
|
||||
@Html(fl!(loader, "author-owned", author = submission.author_name()))
|
||||
})
|
||||
@fl!(loader, "admin-reports-submission")
|
||||
|
||||
@:link(&Link::new_tab(&submission.view_path()).plain(true), {
|
||||
@Html(submission.title())
|
||||
@:link(&Link::new_tab(&submission.view_path()).plain(true), {
|
||||
@Html(submission.title())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@if let Some(comment) = reports_view.comment(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
reported
|
||||
@:link(&Link::new_tab(&comment.author_path()).plain(true), {
|
||||
@Html(fl!(loader, "author-owned", author = comment.author_name()))
|
||||
})
|
||||
@fl!(loader, "admin-reports-comment")
|
||||
}
|
||||
@if let Some(comment) = reports_view.comment(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
reported
|
||||
@:link(&Link::new_tab(&comment.author_path()).plain(true), {
|
||||
@Html(fl!(loader, "author-owned", author = comment.author_name()))
|
||||
})
|
||||
@fl!(loader, "admin-reports-comment")
|
||||
|
||||
@:link(&Link::new_tab(&comment.view_path()).plain(true), {
|
||||
@Html(comment.body())
|
||||
@:link(&Link::new_tab(&comment.view_path()).plain(true), {
|
||||
@Html(comment.body())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
</div>
|
||||
@if let Some(note) = report.note() {
|
||||
<div class="report-description text-section">
|
||||
<h4>@fl!(loader, "admin-reports-note")</h4>
|
||||
<p>@Html(note)</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if let Some(note) = report.note() {
|
||||
<div class="report-description text-section">
|
||||
<h4>@fl!(loader, "admin-reports-note")</h4>
|
||||
<p>@Html(note)</p>
|
||||
</div>
|
||||
}
|
||||
<div class="button-section report-actions">
|
||||
@:button_group(&[
|
||||
Button::secondary(&fl!(loader, "admin-reports-view-button")).href(&reports_view.view_path(report)),
|
||||
|
@ -91,6 +93,73 @@
|
|||
</div>
|
||||
})
|
||||
}
|
||||
@if reports_view.has_open_reports_nav() {
|
||||
@:card_body({
|
||||
@:button_group(&reports_view.open_reports_nav(loader))
|
||||
})
|
||||
}
|
||||
})
|
||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
@:card_title({
|
||||
@fl!(loader, "admin-closed-reports-heading")
|
||||
})
|
||||
@for report in reports_view.closed_reports() {
|
||||
@:card_body({
|
||||
<div class="report">
|
||||
<div class="report-left">
|
||||
<div class="report-head">
|
||||
@if let Some(profile) = reports_view.profile(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
@fl!(loader, "admin-reports-reported")
|
||||
|
||||
@:link(&Link::new_tab(&profile.view_path()).plain(true), {
|
||||
@Html(profile.name())
|
||||
})
|
||||
})
|
||||
}
|
||||
@if let Some(submission) = reports_view.submission(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
@fl!(loader, "admin-reports-reported")
|
||||
|
||||
@:link(&Link::new_tab(&submission.author_path()).plain(true), {
|
||||
@Html(fl!(loader, "author-owned", author = submission.author_name()))
|
||||
})
|
||||
@fl!(loader, "admin-reports-submission")
|
||||
|
||||
@:link(&Link::new_tab(&submission.view_path()).plain(true), {
|
||||
@Html(submission.title())
|
||||
})
|
||||
})
|
||||
}
|
||||
@if let Some(comment) = reports_view.comment(report) {
|
||||
@:reporter(reports_view, report, {
|
||||
reported
|
||||
@:link(&Link::new_tab(&comment.author_path()).plain(true), {
|
||||
@Html(fl!(loader, "author-owned", author = comment.author_name()))
|
||||
})
|
||||
@fl!(loader, "admin-reports-comment")
|
||||
|
||||
@:link(&Link::new_tab(&comment.view_path()).plain(true), {
|
||||
@Html(comment.body())
|
||||
})
|
||||
})
|
||||
}
|
||||
</div>
|
||||
@if let Some(note) = report.note() {
|
||||
<div class="report-description text-section">
|
||||
<h4>@fl!(loader, "admin-reports-note")</h4>
|
||||
<p>@Html(note)</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
@if reports_view.has_closed_reports_nav() {
|
||||
@:card_body({
|
||||
@:button_group(&reports_view.closed_reports_nav(loader))
|
||||
})
|
||||
}
|
||||
})
|
||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
<form method="POST" action="@federation_view.discover_path()">
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
<div class="toolkit-button-group">
|
||||
@:button(&nav_state.submission_button(loader))
|
||||
@:button(&nav_state.browse_button(loader))
|
||||
@if nav_state.is_admin() {
|
||||
@:button(&nav_state.admin_button(loader))
|
||||
}
|
||||
@if nav_state.has_notifications() {
|
||||
@:icon_button("bell", &fl!(loader, "nav-notifications-button"), nav_state.notifications_path())
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
})
|
||||
@:card_body({
|
||||
<p>@fl!(loader, "report-description")</p>
|
||||
@:text_input(&rview.input(loader))
|
||||
@:text_input(&rview.input(loader).dark(nav_state.dark()))
|
||||
})
|
||||
@:card_body({
|
||||
@:button_group(&[
|
||||
|
|
|
@ -296,8 +296,11 @@ server-info-description-input = Description
|
|||
server-info-description-placeholder = Describe your server
|
||||
server-info-submit-button = Save
|
||||
|
||||
admin-reports-heading = Reports
|
||||
admin-closed-reports-heading = Closed Reports
|
||||
admin-reports-heading = Open Reports
|
||||
admin-reports-reported = reported
|
||||
admin-reports-prev = Previous
|
||||
admin-reports-next = Next
|
||||
author-owned = {$author}'s
|
||||
admin-reports-submission = submission:
|
||||
admin-reports-comment = comment:
|
||||
|
|
Loading…
Reference in New Issue