diff --git a/crates/app/src/langs/en-US.toml b/crates/app/src/langs/en-US.toml index fe07c8b..f854057 100644 --- a/crates/app/src/langs/en-US.toml +++ b/crates/app/src/langs/en-US.toml @@ -43,6 +43,7 @@ version = "1.0.0" "general:label.could_not_find_post" = "Could not find original post..." "general:label.timeline_end" = "That's a wrap!" "general:label.loading" = "Working on it!" +"general:label.send_anonymously" = "Send anonymously" "general:label.supporter_motivation" = "Become a supporter!" "general:action.become_supporter" = "Become supporter" diff --git a/crates/app/src/public/html/components.lisp b/crates/app/src/public/html/components.lisp index e863b65..d9001c9 100644 --- a/crates/app/src/public/html/components.lisp +++ b/crates/app/src/public/html/components.lisp @@ -627,7 +627,7 @@ (text "{%- endif %} {%- endmacro %} {% macro question(question, owner, show_community=true, secondary=false, profile=false) -%}") (div ("class" "card question {% if secondary -%}secondary{%- endif %} flex gap-2") - (text "{% if owner.id == 0 -%}") + (text "{% if owner.id == 0 or question.context.mask_owner -%}") (span (text "{% if profile and profile.settings.anonymous_avatar_url -%}") (img @@ -636,7 +636,7 @@ ("class" "avatar shadow") ("loading" "lazy") ("style" "--size: 52px")) - (text "{% else %} {{ self::avatar(username=owner.username, selector_type=\"username\", size=\"52px\") }} {%- endif %}")) + (text "{% else %} {{ self::avatar(username=\"anonymous\", selector_type=\"username\", size=\"52px\") }} {%- endif %}")) (text "{% else %}") (a ("href" "/@{{ owner.username }}") @@ -648,7 +648,7 @@ ("class" "flex items-center gap-2 flex-wrap") (span ("class" "name") - (text "{% if owner.id == 0 -%} {% if profile and profile.settings.anonymous_username -%}") + (text "{% if owner.id == 0 or question.context.mask_owner -%} {% if profile and profile.settings.anonymous_username -%}") (span ("class" "flex items-center gap-2") (b @@ -696,7 +696,7 @@ (text "{{ self::post_media(upload_ids=question.drawings) }}") ; anonymous user ip thing ; this is only shown if the post author is anonymous AND we are a helper - (text "{% if is_helper and owner.id == 0 %}") + (text "{% if is_helper and (owner.id == 0 or question.context.mask_owner) -%}") (details ("class" "card tiny lowered w-full") (summary @@ -705,12 +705,22 @@ (span (text "View IP"))) (pre (code (text "{{ question.ip }}")))) - (text "{% endif %}") + + (text "{% if question.context.mask_owner -%}") + (details + ("class" "card tiny lowered w-full") + (summary + ("class" "w-full flex gap-2 flex-wrap items-center") + (icon (text "venetian-mask")) + (span (text "Unmask"))) + + (text "{{ self::full_username(user=owner) }}")) + (text "{%- endif %} {%- endif %}") ; ... (div ("class" "flex gap-2 items-center justify-between")))) -(text "{%- endmacro %} {% macro create_question_form(receiver=\"0\", community=\"\", header=\"\", is_global=false, drawing_enabled=false) -%}") +(text "{%- endmacro %} {% macro create_question_form(receiver=\"0\", community=\"\", header=\"\", is_global=false, drawing_enabled=false, allow_anonymous=false) -%}") (div ("class" "card-nest") (div @@ -742,50 +752,66 @@ ("minlength" "2") ("maxlength" "4096"))) (div - ("class" "flex gap-2") - (button - ("class" "primary") - (text "{{ text \"communities:action.create\" }}")) + ("class" "flex w-full justify-between gap-2 flex-collapse") + (div + ("class" "flex gap-2") + (button + ("class" "primary") + (text "{{ text \"communities:action.create\" }}")) - (text "{% if drawing_enabled -%}") - (button - ("class" "lowered") - ("ui_ident" "add_drawing") - ("onclick" "attach_drawing()") - ("type" "button") - (text "{{ text \"communities:action.draw\" }}")) + (text "{% if drawing_enabled -%}") + (button + ("class" "lowered") + ("ui_ident" "add_drawing") + ("onclick" "attach_drawing()") + ("type" "button") + (text "{{ text \"communities:action.draw\" }}")) - (button - ("class" "lowered red hidden") - ("ui_ident" "remove_drawing") - ("onclick" "remove_drawing()") - ("type" "button") - (text "{{ text \"communities:action.remove_drawing\" }}")) + (button + ("class" "lowered red hidden") + ("ui_ident" "remove_drawing") + ("onclick" "remove_drawing()") + ("type" "button") + (text "{{ text \"communities:action.remove_drawing\" }}")) - (script - (text "globalThis.attach_drawing = async () => { - globalThis.gerald = await trigger(\"carp::new\", [document.querySelector(\"[ui_ident=carp_canvas_field]\")]); - globalThis.gerald.create_canvas(); + (script + (text "globalThis.attach_drawing = async () => { + globalThis.gerald = await trigger(\"carp::new\", [document.querySelector(\"[ui_ident=carp_canvas_field]\")]); + globalThis.gerald.create_canvas(); - document.querySelector(\"[ui_ident=add_drawing]\").classList.add(\"hidden\"); - document.querySelector(\"[ui_ident=remove_drawing]\").classList.remove(\"hidden\"); - } - - globalThis.remove_drawing = async () => { - if ( - !(await trigger(\"atto::confirm\", [ - \"Are you sure you would like to do this?\", - ])) - ) { - return; + document.querySelector(\"[ui_ident=add_drawing]\").classList.add(\"hidden\"); + document.querySelector(\"[ui_ident=remove_drawing]\").classList.remove(\"hidden\"); } - document.querySelector(\"[ui_ident=carp_canvas_field]\").innerHTML = \"\"; - globalThis.gerald = null; + globalThis.remove_drawing = async () => { + if ( + !(await trigger(\"atto::confirm\", [ + \"Are you sure you would like to do this?\", + ])) + ) { + return; + } - document.querySelector(\"[ui_ident=add_drawing]\").classList.remove(\"hidden\"); - document.querySelector(\"[ui_ident=remove_drawing]\").classList.add(\"hidden\"); - }")) + document.querySelector(\"[ui_ident=carp_canvas_field]\").innerHTML = \"\"; + globalThis.gerald = null; + + document.querySelector(\"[ui_ident=add_drawing]\").classList.remove(\"hidden\"); + document.querySelector(\"[ui_ident=remove_drawing]\").classList.add(\"hidden\"); + }")) + (text "{%- endif %}")) + + (text "{% if not is_global and allow_anonymous -%}") + (div + ("class" "flex gap-2 items-center") + (input + ("type" "checkbox") + ("name" "mask_owner") + ("id" "mask_owner") + ("class" "w-content")) + + (label + ("for" "mask_owner") + (b (str (text "general:label.send_anonymously"))))) (text "{%- endif %}")))) (script @@ -811,6 +837,7 @@ receiver: \"{{ receiver }}\", community: \"{{ community }}\", is_global: \"{{ is_global }}\" == \"true\", + mask_owner: (e.target.mask_owner || { checked:false }).checked }), ); @@ -1136,12 +1163,12 @@ (str (text "general:link.reference"))) (button - ("onclick" "trigger('me::achievement', ['OpenTos']); Turbo.visit('{{ config.policies.terms_of_service }}')") + ("onclick" "trigger('me::achievement_link', ['OpenTos', '{{ config.policies.terms_of_service }}'])") (icon (text "heart-handshake")) (text "Terms of service")) (button - ("onclick" "trigger('me::achievement', ['OpenPrivacyPolicy']); Turbo.visit('{{ config.policies.privacy }}')") + ("onclick" "trigger('me::achievement_link', ['OpenPrivacyPolicy', '{{ config.policies.privacy }}'])") (icon (text "cookie")) (text "Privacy policy")) (b ("class" "title") (str (text "general:label.account"))) diff --git a/crates/app/src/public/html/profile/media.lisp b/crates/app/src/public/html/profile/media.lisp index a3888dc..b4bf80c 100644 --- a/crates/app/src/public/html/profile/media.lisp +++ b/crates/app/src/public/html/profile/media.lisp @@ -1,7 +1,7 @@ (text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}") (div ("style" "display: contents") - (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings) }}")) + (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings, allow_anonymous=profile.settings.allow_anonymous_questions) }}")) (text "{%- endif %} {{ macros::profile_nav(selected=\"media\") }}") (div diff --git a/crates/app/src/public/html/profile/outbox.lisp b/crates/app/src/public/html/profile/outbox.lisp index d77d314..7dbcabb 100644 --- a/crates/app/src/public/html/profile/outbox.lisp +++ b/crates/app/src/public/html/profile/outbox.lisp @@ -1,7 +1,7 @@ (text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}") (div ("style" "display: contents") - (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings) }}")) + (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings, allow_anonymous=profile.settings.allow_anonymous_questions) }}")) (text "{%- endif %} {{ macros::profile_nav(selected=\"outbox\") }}") (div diff --git a/crates/app/src/public/html/profile/posts.lisp b/crates/app/src/public/html/profile/posts.lisp index 6120fea..183aeca 100644 --- a/crates/app/src/public/html/profile/posts.lisp +++ b/crates/app/src/public/html/profile/posts.lisp @@ -1,7 +1,7 @@ (text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}") (div ("style" "display: contents") - (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings) }}")) + (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings, allow_anonymous=profile.settings.allow_anonymous_questions) }}")) (text "{%- endif %} {% if not tag and pinned|length != 0 -%}") (div diff --git a/crates/app/src/public/html/profile/replies.lisp b/crates/app/src/public/html/profile/replies.lisp index 4afe348..5f70d68 100644 --- a/crates/app/src/public/html/profile/replies.lisp +++ b/crates/app/src/public/html/profile/replies.lisp @@ -1,7 +1,7 @@ (text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}") (div ("style" "display: contents") - (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings) }}")) + (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings, allow_anonymous=profile.settings.allow_anonymous_questions) }}")) (text "{%- endif %} {{ macros::profile_nav(selected=\"replies\") }}") (div diff --git a/crates/app/src/public/js/atto.js b/crates/app/src/public/js/atto.js index 85c9c2c..30d29bd 100644 --- a/crates/app/src/public/js/atto.js +++ b/crates/app/src/public/js/atto.js @@ -917,18 +917,18 @@ media_theme_pref(); if (option.input_element_type === "checkbox") { into_element.innerHTML += `
- + - -
`; + + `; return; } diff --git a/crates/app/src/public/js/me.js b/crates/app/src/public/js/me.js index 0c7ac10..4fd2150 100644 --- a/crates/app/src/public/js/me.js +++ b/crates/app/src/public/js/me.js @@ -342,25 +342,34 @@ }, ); - self.define("achievement", async (_, name) => { - fetch("/api/v1/auth/user/me/achievement", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - name, - }), - }) - .then((res) => res.json()) - .then((res) => { - if (!res.ok) { - trigger("atto::toast", [ - res.ok ? "success" : "error", - res.message, - ]); - } - }); + self.define("achievement", (_, name) => { + return new Promise((resolve) => { + fetch("/api/v1/auth/user/me/achievement", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + name, + }), + }) + .then((res) => res.json()) + .then((res) => { + if (!res.ok) { + trigger("atto::toast", [ + res.ok ? "success" : "error", + res.message, + ]); + } + + resolve(); + }); + }); + }); + + self.define("achievement_link", async (_, name, href) => { + await self.achievement(name); + Turbo.visit(href); }); self.define("report", (_, asset, asset_type) => { diff --git a/crates/app/src/routes/api/v1/communities/questions.rs b/crates/app/src/routes/api/v1/communities/questions.rs index 13566e5..631e5c0 100644 --- a/crates/app/src/routes/api/v1/communities/questions.rs +++ b/crates/app/src/routes/api/v1/communities/questions.rs @@ -92,6 +92,10 @@ pub async fn create_request( } } + if req.mask_owner && !req.is_global { + props.context.mask_owner = true; + } + match data .create_question(props, drawings.iter().map(|x| x.to_vec()).collect()) .await diff --git a/crates/app/src/routes/api/v1/mod.rs b/crates/app/src/routes/api/v1/mod.rs index 3cd4423..c88b003 100644 --- a/crates/app/src/routes/api/v1/mod.rs +++ b/crates/app/src/routes/api/v1/mod.rs @@ -790,6 +790,8 @@ pub struct CreateQuestion { pub receiver: String, #[serde(default)] pub community: String, + #[serde(default)] + pub mask_owner: bool, } #[derive(Deserialize)] diff --git a/crates/core/src/model/communities.rs b/crates/core/src/model/communities.rs index e2c4261..4df9795 100644 --- a/crates/core/src/model/communities.rs +++ b/crates/core/src/model/communities.rs @@ -381,6 +381,9 @@ impl Question { pub struct QuestionContext { #[serde(default)] pub is_nsfw: bool, + /// If the owner is shown as anonymous in the UI. + #[serde(default)] + pub mask_owner: bool, } #[derive(Clone, Debug, Serialize, Deserialize)]