diff --git a/src/browse.rs b/src/browse.rs index c3a330f..b6165a4 100644 --- a/src/browse.rs +++ b/src/browse.rs @@ -66,10 +66,16 @@ impl ViewBrowseState { ) -> Result { let store = state.profiles.clone(); + let can_view_sensitive = if let Some(viewer) = viewer { + state.settings.for_profile(viewer).await?.sensitive + } else { + false + }; + let state = actix_web::web::block(move || { let mut cache = Cache::new(); - let page = browse_page(viewer, &store, &mut cache, source); + let page = browse_page(viewer, can_view_sensitive, &store, &mut cache, source); Ok(ViewBrowseState { cache, diff --git a/src/comments/mod.rs b/src/comments/mod.rs index 6cc078a..ed69472 100644 --- a/src/comments/mod.rs +++ b/src/comments/mod.rs @@ -163,16 +163,16 @@ fn can_view_logged_out( None => return Ok(false), }; - if submission.is_followers_only() { - return Ok(false); - } - - let submissioner = match store.store.profiles.by_id(submission.profile_id())? { - Some(s) => s, - None => return Ok(false), - }; - - if submissioner.login_required() { + if crate::pagination::submission::can_view( + None, + &submission, + &store.store, + &mut Default::default(), + true, + false, + ) + .is_none() + { return Ok(false); } @@ -217,6 +217,7 @@ fn can_view_comment_logged_out_no_recurse( fn can_view( profile: &Profile, + can_view_sensitive: bool, comment: &Comment, store: &hyaenidae_profiles::State, ) -> Result { @@ -225,47 +226,19 @@ fn can_view( None => return Ok(false), }; - let submissioner_id = submission.profile_id(); - - let blocking_submissioner = store - .store - .view - .blocks - .by_forward(submissioner_id, profile.id())? - .is_some(); - - if blocking_submissioner { + if crate::pagination::submission::can_view( + Some(profile.id()), + &submission, + &store.store, + &mut Default::default(), + true, + can_view_sensitive, + ) + .is_none() + { return Ok(false); } - let blocked_by_submissioner = store - .store - .view - .blocks - .by_forward(profile.id(), submissioner_id)? - .is_some(); - - if blocked_by_submissioner { - return Ok(false); - } - - if submission.is_followers_only() { - let is_submissioner = profile.id() == submissioner_id; - - if !is_submissioner { - let follows_submissioner = store - .store - .view - .follows - .by_forward(submissioner_id, profile.id())? - .is_some(); - - if !follows_submissioner { - return Ok(false); - } - } - } - can_view_comment(profile, comment, store) } @@ -325,8 +298,14 @@ async fn prepare_view( profile: Option<&Profile>, state: &State, ) -> Result, Error> { + let can_view_sensitive = if let Some(profile) = profile { + state.settings.for_profile(profile.id()).await?.sensitive + } else { + false + }; + match profile { - Some(profile) if !can_view(&profile, &comment, &state.profiles)? => { + Some(profile) if !can_view(&profile, can_view_sensitive, &comment, &state.profiles)? => { return Ok(None); } None if !can_view_logged_out(&comment, &state.profiles)? => { @@ -550,7 +529,9 @@ async fn reply( None => return Ok(crate::to_404()), }; - if !can_view(&profile, &comment, &state.profiles)? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if !can_view(&profile, can_view_sensitive, &comment, &state.profiles)? { return Ok(crate::to_404()); } @@ -738,7 +719,9 @@ async fn report_page( None => return Ok(crate::to_404()), }; - if !can_view(&profile, &comment, &state.profiles)? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if !can_view(&profile, can_view_sensitive, &comment, &state.profiles)? { return Ok(crate::to_404()); } @@ -775,7 +758,9 @@ async fn report( None => return Ok(crate::to_404()), }; - if !can_view(&profile, &comment, &state.profiles)? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if !can_view(&profile, can_view_sensitive, &comment, &state.profiles)? { return Ok(crate::to_404()); } @@ -828,7 +813,9 @@ async fn report_success_page( None => return Ok(crate::to_404()), }; - if !can_view(&profile, &comment, &state.profiles)? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if !can_view(&profile, can_view_sensitive, &comment, &state.profiles)? { return Ok(crate::to_404()); } diff --git a/src/main.rs b/src/main.rs index 43c0ac2..39a4074 100644 --- a/src/main.rs +++ b/src/main.rs @@ -221,6 +221,7 @@ struct State { spawn: jobs::Spawn, apub: apub::Apub, images: images::Images, + settings: profiles::SettingStore, domain: String, base_url: url::Url, db: Db, @@ -240,6 +241,7 @@ impl State { let domain = base_url.domain().req()?.to_owned(); let admin = admin::Store::build(db)?; + let settings = profiles::SettingStore::build(db)?; Ok(State { profiles: hyaenidae_profiles::State::build( @@ -254,6 +256,7 @@ impl State { spawn, apub, images, + settings, base_url, domain, db: db.clone(), diff --git a/src/nav.rs b/src/nav.rs index 3151ff4..83f1b57 100644 --- a/src/nav.rs +++ b/src/nav.rs @@ -63,6 +63,12 @@ impl FromRequest for NavState { format!("{}?show_nav=true", path) }; + let dark = if let Some(profile) = &profile { + state.settings.for_profile(profile.id()).await?.dark + } else { + true + }; + let notification_count = if let Some(profile) = &profile { total_for_profile(profile.id(), &state).await.ok() } else { @@ -99,7 +105,7 @@ impl FromRequest for NavState { admin, href, is_open, - dark: true, + dark, }) }) } diff --git a/src/pagination/submission.rs b/src/pagination/submission.rs index b69f75e..3135533 100644 --- a/src/pagination/submission.rs +++ b/src/pagination/submission.rs @@ -22,12 +22,14 @@ impl Cache { 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, @@ -39,6 +41,7 @@ pub(crate) fn browse_page( pub(crate) fn draft_page( viewer: Option, + can_view_sensitive: bool, profile_id: Uuid, store: &hyaenidae_profiles::State, cache: &mut Cache, @@ -46,6 +49,7 @@ pub(crate) fn draft_page( ) -> Page { Page::from_pagination( DraftPager { + can_view_sensitive, viewer, profile_id, store, @@ -58,6 +62,7 @@ pub(crate) fn draft_page( pub(crate) fn main_page( viewer: Option, + can_view_sensitive: bool, profile_id: Uuid, store: &hyaenidae_profiles::State, cache: &mut Cache, @@ -65,6 +70,7 @@ pub(crate) fn main_page( ) -> Page { Page::from_pagination( SubmissionPager { + can_view_sensitive, viewer, profile_id, store, @@ -76,6 +82,7 @@ pub(crate) fn main_page( } struct BrowsePager<'b> { + can_view_sensitive: bool, viewer: Option, store: &'b hyaenidae_profiles::State, cache: &'b mut Cache, @@ -93,6 +100,7 @@ impl<'b> Pagination for BrowsePager<'b> { &self.store.store, self.cache, false, + self.can_view_sensitive, )), ) } @@ -108,6 +116,7 @@ impl<'b> Pagination for BrowsePager<'b> { &self.store.store, self.cache, false, + self.can_view_sensitive, )), ) } @@ -123,12 +132,14 @@ impl<'b> Pagination for BrowsePager<'b> { &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, @@ -147,6 +158,7 @@ impl<'b> Pagination for DraftPager<'b> { &self.store.store, self.cache, true, + self.can_view_sensitive, )), ) } @@ -162,6 +174,7 @@ impl<'b> Pagination for DraftPager<'b> { &self.store.store, self.cache, true, + self.can_view_sensitive, )), ) } @@ -177,12 +190,14 @@ impl<'b> Pagination for DraftPager<'b> { &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, @@ -201,6 +216,7 @@ impl<'b> Pagination for SubmissionPager<'b> { &self.store.store, self.cache, true, + self.can_view_sensitive, )), ) } @@ -216,6 +232,7 @@ impl<'b> Pagination for SubmissionPager<'b> { &self.store.store, self.cache, true, + self.can_view_sensitive, )), ) } @@ -231,6 +248,7 @@ impl<'b> Pagination for SubmissionPager<'b> { &self.store.store, self.cache, true, + self.can_view_sensitive, )), ) } @@ -241,6 +259,7 @@ fn filter_submissions<'a>( 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) { @@ -251,7 +270,14 @@ fn filter_submissions<'a>( cache.profile_map.insert(profile.id(), profile); } - let opt = can_view(viewer, &submission, store, cache, show_unlisted); + 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) { @@ -266,7 +292,14 @@ fn filter_submissions<'a>( } else { let submission = cache.submission_map.get(&submission_id)?.clone(); - can_view(viewer, &submission, store, cache, show_unlisted)?; + can_view( + viewer, + &submission, + store, + cache, + show_unlisted, + can_view_sensitive, + )?; Some(submission_id) } @@ -279,11 +312,17 @@ pub(crate) fn can_view( 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; diff --git a/src/profiles/mod.rs b/src/profiles/mod.rs index 8644aa6..1890b54 100644 --- a/src/profiles/mod.rs +++ b/src/profiles/mod.rs @@ -15,9 +15,11 @@ use i18n_embed_fl::fl; use std::collections::HashMap; use uuid::Uuid; +mod settings; mod state; mod update; +pub(crate) use settings::{SettingStore, Settings}; pub use state::{EditProfileState, ViewProfileState}; pub use update::HandleState; diff --git a/src/profiles/settings.rs b/src/profiles/settings.rs new file mode 100644 index 0000000..015d2d3 --- /dev/null +++ b/src/profiles/settings.rs @@ -0,0 +1,52 @@ +use crate::error::Error; +use sled::{Db, Tree}; +use uuid::Uuid; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub(crate) struct Settings { + pub(crate) dark: bool, + pub(crate) sensitive: bool, +} + +#[derive(Clone, Debug)] +pub(crate) struct SettingStore { + tree: Tree, +} + +impl SettingStore { + pub(crate) fn build(db: &Db) -> Result { + Ok(SettingStore { + tree: db.open_tree("/main/settings")?, + }) + } + + pub(crate) async fn for_profile(&self, profile_id: Uuid) -> Result { + let this = self.clone(); + + let opt = actix_web::web::block(move || { + Ok(this + .tree + .get(profile_id.as_bytes())? + .and_then(|ivec| serde_json::from_slice(&ivec).ok())) + }) + .await?; + + Ok(opt.unwrap_or(Settings { + dark: true, + sensitive: false, + })) + } + + pub(crate) async fn update(&self, profile_id: Uuid, settings: Settings) -> Result<(), Error> { + let this = self.clone(); + + actix_web::web::block(move || { + let vec = serde_json::to_vec(&settings)?; + this.tree.insert(profile_id.as_bytes(), vec)?; + Ok(()) as Result<_, Error> + }) + .await?; + + Ok(()) + } +} diff --git a/src/profiles/state.rs b/src/profiles/state.rs index 231b705..8142cff 100644 --- a/src/profiles/state.rs +++ b/src/profiles/state.rs @@ -6,6 +6,7 @@ use crate::{ submission::{draft_page, main_page, Cache}, PageSource, }, + profiles::settings::Settings, views::ProfileView, ActixLoader, State, }; @@ -44,6 +45,8 @@ pub struct EditProfileState { pub(crate) banner_error: Option, login_required_value: Option, pub(crate) login_required_error: Option, + pub(crate) settings_error: Option, + pub(crate) settings: Settings, } impl ViewProfileState { @@ -226,6 +229,12 @@ impl ViewProfileState { ) -> Result { let store = state.profiles.clone(); + let can_view_sensitive = if let Some(viewer) = viewer { + state.settings.for_profile(viewer).await?.sensitive + } else { + false + }; + let state = actix_web::web::block(move || { let mut cache = Cache::new(); @@ -248,9 +257,23 @@ impl ViewProfileState { cache.profile_map.insert(profile.id(), profile); let page = if drafts { - draft_page(viewer, profile_id, &store, &mut cache, source) + draft_page( + viewer, + can_view_sensitive, + profile_id, + &store, + &mut cache, + source, + ) } else { - main_page(viewer, profile_id, &store, &mut cache, source) + main_page( + viewer, + can_view_sensitive, + profile_id, + &store, + &mut cache, + source, + ) }; let is_self = viewer.map(|id| id == profile_id).unwrap_or(false); @@ -308,7 +331,12 @@ impl ViewProfileState { } impl EditProfileState { - pub(super) fn new(profile: Profile, icon: Option, banner: Option) -> Self { + pub(super) fn new( + profile: Profile, + icon: Option, + banner: Option, + settings: Settings, + ) -> Self { EditProfileState { profile, icon, @@ -321,6 +349,8 @@ impl EditProfileState { banner_error: None, login_required_value: None, login_required_error: None, + settings_error: None, + settings, } } @@ -330,6 +360,8 @@ impl EditProfileState { let icon_id = profile.icon(); let banner_id = profile.banner(); + let settings = state.settings.for_profile(profile.id()).await?; + let (icon, banner) = actix_web::web::block(move || { let icon = if let Some(id) = icon_id { store.store.files.by_id(id)? @@ -346,7 +378,7 @@ impl EditProfileState { }) .await?; - Ok(Self::new(profile, icon, banner)) + Ok(Self::new(profile, icon, banner, settings)) } pub(crate) fn profile<'a>(&'a self) -> ProfileView<'a> { @@ -497,4 +529,9 @@ impl EditProfileState { self.login_required_error = Some(err.to_owned()); self } + + pub(super) fn settings_error(&mut self, err: String) -> &mut Self { + self.settings_error = Some(err); + self + } } diff --git a/src/profiles/update.rs b/src/profiles/update.rs index f6f8074..346532f 100644 --- a/src/profiles/update.rs +++ b/src/profiles/update.rs @@ -2,7 +2,7 @@ use crate::{ error::{Error, OptionExt}, middleware::{ProfileData, UserProfile}, nav::NavState, - profiles::{state::EditProfileState, to_current_profile}, + profiles::{settings::Settings, state::EditProfileState, to_current_profile}, ActixLoader, State, }; use actix_session::Session; @@ -66,6 +66,11 @@ pub(super) fn update_scope() -> Scope { .route(web::post().to(update_require_login)) .route(web::get().to(to_current_profile)), ) + .service( + web::resource("/settings") + .route(web::post().to(update_settings)) + .route(web::get().to(to_current_profile)), + ) } pub(super) fn to_create() -> HttpResponse { @@ -328,6 +333,39 @@ async fn update_require_login( }) } +#[derive(serde::Deserialize)] +struct SettingsForm { + sensitive: Option, + dark: Option, +} + +async fn update_settings( + loader: ActixLoader, + form: web::Form, + profile: UserProfile, + nav_state: NavState, + state: web::Data, +) -> Result { + let profile = profile.0; + + let settings = Settings { + sensitive: form.sensitive.is_some(), + dark: form.dark.is_some(), + }; + + let error = match state.settings.update(profile.id(), settings).await { + Ok(_) => return Ok(to_current_profile()), + Err(e) => e.to_string(), + }; + + let mut profile_state = EditProfileState::for_profile(profile, &state).await?; + profile_state.settings_error(error); + + crate::rendered(HttpResponse::InternalServerError(), |cursor| { + crate::templates::profiles::current(cursor, &loader, &profile_state, &nav_state) + }) +} + #[derive(Clone, Debug, serde::Deserialize)] pub struct HandleForm { pub(crate) handle: String, diff --git a/src/submissions.rs b/src/submissions.rs index 4892f87..4d7d89e 100644 --- a/src/submissions.rs +++ b/src/submissions.rs @@ -5,6 +5,7 @@ use crate::{ images::{largest_image, FullImage, ImageType}, middleware::{CurrentSubmission, UserProfile}, nav::NavState, + profiles::Settings, views::{OwnedProfileView, OwnedSubmissionView}, ActixLoader, State, }; @@ -195,7 +196,16 @@ async fn report_page( let view = ReportView::build(submission_id.into_inner(), &state).await?; - if let Some(res) = can_view(Some(profile.id()), &view.author, &view.submission, &state).await? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if let Some(res) = can_view( + Some(profile.id()), + can_view_sensitive, + &view.submission, + &state, + ) + .await? + { return Ok(res); } @@ -223,7 +233,16 @@ async fn report( let mut view = ReportView::build(submission_id.into_inner(), &state).await?; - if let Some(res) = can_view(Some(profile.id()), &view.author, &view.submission, &state).await? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if let Some(res) = can_view( + Some(profile.id()), + can_view_sensitive, + &view.submission, + &state, + ) + .await? + { return Ok(res); } @@ -267,7 +286,16 @@ async fn report_success_page( let view = ReportView::build(submission_id.into_inner(), &state).await?; - if let Some(res) = can_view(Some(profile.id()), &view.author, &view.submission, &state).await? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if let Some(res) = can_view( + Some(profile.id()), + can_view_sensitive, + &view.submission, + &state, + ) + .await? + { return Ok(res); } @@ -293,12 +321,15 @@ pub struct SubmissionState { pub(crate) delete_error: Option, pub(crate) visibility_error: Option, pub(crate) sensitive_error: Option, + pub(crate) settings: Settings, } impl SubmissionState { async fn new(submission: Submission, state: &State) -> Result { let file_ids: Vec = submission.files().iter().copied().collect(); + let settings = state.settings.for_profile(submission.profile_id()).await?; + let store = state.profiles.clone(); let files = web::block(move || { let mut files = HashMap::new(); @@ -327,6 +358,7 @@ impl SubmissionState { delete_error: None, visibility_error: None, sensitive_error: None, + settings, }) } @@ -613,52 +645,29 @@ impl ViewSubmissionState { async fn can_view( viewer: Option, - poster: &Profile, + can_view_sensitive: bool, submission: &Submission, state: &State, ) -> Result, Error> { - if submission.is_sensitive() { - if viewer.is_none() { - return Ok(Some(crate::to_404())); - } - } + let store = state.profiles.clone(); + let submission = submission.clone(); + let opt = web::block(move || { + Ok(crate::pagination::submission::can_view( + viewer, + &submission, + &store.store, + &mut Default::default(), + true, + can_view_sensitive, + )) as Result, Error> + }) + .await?; - if poster.login_required() && viewer.is_none() { + if opt.is_none() { return Ok(Some(crate::to_404())); - } - - if poster.local_owner().is_none() && viewer.is_none() { - return Ok(Some(crate::to_404())); - } - - let is_self = viewer - .as_ref() - .map(|pid| *pid == poster.id()) - .unwrap_or(false); - - if submission.published().is_none() && !is_self { - return Ok(Some(crate::to_404())); - } - - let is_follower_or_self = if let Some(pid) = viewer { - let is_follower = state - .profiles - .store - .view - .follows - .by_forward(poster.id(), pid)? - .is_some(); - - is_follower || is_self } else { - false - }; - - if submission.is_followers_only() && !is_follower_or_self { - return Ok(Some(crate::to_404())); + Ok(None) } - - Ok(None) } async fn files_for_submission( @@ -746,6 +755,7 @@ fn submission_nav( async fn adjacent_submissions( viewer: Option, + can_view_sensitive: bool, submission: &Submission, state: &State, ) -> Result<(Option, Option), Error> { @@ -769,6 +779,7 @@ async fn adjacent_submissions( &inner_store.store, &mut Default::default(), true, + can_view_sensitive, ) .map(move |_| id) }); @@ -787,6 +798,7 @@ async fn adjacent_submissions( &store.store, &mut Default::default(), true, + can_view_sensitive, ) .map(move |_| id) }); @@ -808,6 +820,7 @@ async fn adjacent_submissions( &inner_store.store, &mut Default::default(), true, + can_view_sensitive, ) .map(move |_| id) }); @@ -826,6 +839,7 @@ async fn adjacent_submissions( &store.store, &mut Default::default(), true, + can_view_sensitive, ) .map(move |_| id) }); @@ -862,12 +876,18 @@ async fn submission_page( let cache = files_for_submission(&submission, cache, &state).await?; let cache = files_for_profile(&poster, cache, &state).await?; - if let Some(res) = can_view(viewer, &poster, &submission, &state).await? { + let can_view_sensitive = if let Some(viewer) = viewer { + state.settings.for_profile(viewer).await?.sensitive + } else { + false + }; + + if let Some(res) = can_view(viewer, can_view_sensitive, &submission, &state).await? { return Ok(res); } let (next_submission, previous_submission) = - adjacent_submissions(viewer, &submission, &state).await?; + adjacent_submissions(viewer, can_view_sensitive, &submission, &state).await?; let (nav, current_file) = submission_nav( page.map(|p| p.into_inner()), @@ -969,7 +989,10 @@ async fn create_comment( let poster_id = submission.profile_id(); let poster = web::block(move || store.store.profiles.by_id(poster_id)?.req()).await?; - if let Some(res) = can_view(Some(profile.id()), &poster, &submission, &state).await? { + let can_view_sensitive = state.settings.for_profile(profile.id()).await?.sensitive; + + if let Some(res) = can_view(Some(profile.id()), can_view_sensitive, &submission, &state).await? + { return Ok(res); } @@ -999,7 +1022,7 @@ async fn create_comment( let cache = files_for_profile(&poster, cache, &state).await?; let (next_submission, previous_submission) = - adjacent_submissions(Some(profile.id()), &submission, &state).await?; + adjacent_submissions(Some(profile.id()), can_view_sensitive, &submission, &state).await?; let (nav, current_file) = submission_nav( page.map(|p| p.into_inner()), diff --git a/templates/profiles/current.rs.html b/templates/profiles/current.rs.html index 68b86a8..0dd175d 100644 --- a/templates/profiles/current.rs.html +++ b/templates/profiles/current.rs.html @@ -30,6 +30,27 @@ @:button_group(&state.buttons(loader)) }) }) + @:card(&Card::full_width().dark(nav_state.dark()), { +
+ @:card_title({ + @fl!(loader, "update-settings-heading") + }) + @if let Some(error) = &state.settings_error { + @:card_body({ + @error + }) + } + @:card_body({ + @:checkbox("sensitive", &fl!(loader, "sensitive-checkbox"), state.settings.sensitive) + @:checkbox("dark", &fl!(loader, "dark-checkbox"), state.settings.dark) + }) + @:card_body({ + @:button_group(&[ + Button::primary(&fl!(loader, "update-settings-button")), + ]) + }) +
+ }) @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ @fl!(loader, "update-profile-heading") diff --git a/templates/submissions/update.rs.html b/templates/submissions/update.rs.html index c9a34a8..19fc10e 100644 --- a/templates/submissions/update.rs.html +++ b/templates/submissions/update.rs.html @@ -70,21 +70,23 @@ }) } - @:card(&Card::full_width().dark(nav_state.dark()), { -
- @:card_title({ - @fl!(loader, "submission-sensitive-heading") - }) - @:card_body({ - @:checkbox("sensitive", &fl!(loader, "submission-sensitive-checkbox"), state.submission.is_sensitive()) - }) - @:card_body({ - @:button_group(&[ - Button::primary(&fl!(loader, "submission-sensitive-button")), - ]) - }) -
- }) + @if state.settings.sensitive { + @:card(&Card::full_width().dark(nav_state.dark()), { +
+ @:card_title({ + @fl!(loader, "submission-sensitive-heading") + }) + @:card_body({ + @:checkbox("sensitive", &fl!(loader, "submission-sensitive-checkbox"), state.submission.is_sensitive()) + }) + @:card_body({ + @:button_group(&[ + Button::primary(&fl!(loader, "submission-sensitive-button")), + ]) + }) +
+ }) + } @:card(&Card::full_width().dark(nav_state.dark()), {
@:card_title({ diff --git a/translations/en-US/hyaenidae.ftl b/translations/en-US/hyaenidae.ftl index 3c9e627..ec97908 100644 --- a/translations/en-US/hyaenidae.ftl +++ b/translations/en-US/hyaenidae.ftl @@ -138,6 +138,12 @@ file-num = file {$num} profile-settings-title = Profile Settings profile-settings-subtitle = Edit {$profileName}'s profile on {site-name} + +update-settings-heading = Update Profile Settings +sensitive-checkbox = Show me NSFW content +dark-checkbox = Enable dark mode +update-settings-button = Save + update-profile-heading = Update Profile update-bio-heading = Update Bio update-bio-description =