From 7a31dcbd9bfc97b5139bb49c649045ed8b90b8d7 Mon Sep 17 00:00:00 2001 From: trisua Date: Sun, 10 Aug 2025 00:17:21 -0400 Subject: [PATCH] add: product thumbnails ui --- crates/app/src/langs/en-US.toml | 1 + crates/app/src/public/css/root.css | 3 +- .../src/public/html/communities/settings.lisp | 3 +- crates/app/src/public/html/components.lisp | 15 +-- crates/app/src/public/html/economy/edit.lisp | 100 +++++++++++++++++- .../app/src/public/html/economy/product.lisp | 1 + 6 files changed, 111 insertions(+), 12 deletions(-) diff --git a/crates/app/src/langs/en-US.toml b/crates/app/src/langs/en-US.toml index 4e5aaf8..c9e1772 100644 --- a/crates/app/src/langs/en-US.toml +++ b/crates/app/src/langs/en-US.toml @@ -349,3 +349,4 @@ version = "1.0.0" "economy:label.snippet_data" = "Snippet data" "economy:action.apply" = "Apply" "economy:action.unapply" = "Unapply" +"economy:label.thumbnails" = "Thumbnails" diff --git a/crates/app/src/public/css/root.css b/crates/app/src/public/css/root.css index 686cd49..1a53a2e 100644 --- a/crates/app/src/public/css/root.css +++ b/crates/app/src/public/css/root.css @@ -201,8 +201,7 @@ p { .name { max-width: 250px; overflow: hidden; - /* overflow-wrap: break-word; */ - overflow-wrap: anywhere; + white-space: nowrap; text-overflow: ellipsis; } diff --git a/crates/app/src/public/html/communities/settings.lisp b/crates/app/src/public/html/communities/settings.lisp index d55a324..7245a87 100644 --- a/crates/app/src/public/html/communities/settings.lisp +++ b/crates/app/src/public/html/communities/settings.lisp @@ -476,8 +476,7 @@ (text "{{ text \"communities:action.create\" }}")) (span ("class" "fade") - (text "Emojis can be a maximum of 256 KiB, or 512x512px (width x - height).")))) + (text "Emojis can be a maximum of 256 KiB.")))) (text "{% for emoji in emojis %}") (div ("class" "card secondary flex flex_wrap gap_2 items_center justify_between") diff --git a/crates/app/src/public/html/components.lisp b/crates/app/src/public/html/components.lisp index 706a594..c7fe864 100644 --- a/crates/app/src/public/html/components.lisp +++ b/crates/app/src/public/html/components.lisp @@ -87,13 +87,13 @@ (span (text "{{ dislikes }}")) (text "{%- endif %}")) -(text "{%- endif %} {%- endmacro %} {% macro full_username(user, wrap=true) -%} {% if user and user.username -%}") +(text "{%- endif %} {%- endmacro %} {% macro full_username(user, wrap=true, max_width=\"180px\") -%} {% if user and user.username -%}") (div ("class" "flex {% if wrap -%} flex_wrap {%- endif %} items_center") (a ("href" "/@{{ user.username }}") - ("class" "flush flex gap_1") - ("style" "font-weight: 600") + ("class" "name flush flex gap_1") + ("style" "font-weight: 600; max-width: {{ max_width }}") ("target" "_top") (text "{% if user.settings.private_profile -%}") (span @@ -384,7 +384,7 @@ (text "{{ self::avatar(username=owner.username, size=\"24px\", selector_type=\"username\") }}")) (text "{%- endif %}") (span - ("class" "name") + ; ("class" "name") (text "{{ self::full_username(user=owner) }}")) (text "{{ self::post_info(post=post, community=community) }}") (text "{% if post.context.is_pinned or post.context.is_profile_pinned -%} {{ icon \"pin\" }} {%- endif %}")) @@ -465,14 +465,15 @@ (text "{% if community and show_community and community.id != config.town_square or question %}")) (text "{%- endif %} {%- endmacro %}") -(text "{% macro post_media(upload_ids) -%} {% if upload_ids|length > 0 -%}") +(text "{% macro post_media(upload_ids, custom_click=false) -%} {% if upload_ids|length > 0 -%}") (div ("class" "media_gallery gap_2") (text "{% for upload in upload_ids %}") (img ("src" "/api/v1/uploads/{{ upload }}") + ("data-upload-id" "{{ upload }}") ("alt" "Image upload") - ("onclick" "trigger('ui::lightbox_open', ['/api/v1/uploads/{{ upload }}'])")) + ("onclick" "{% if custom_click -%} {{ custom_click }} {%- else -%} trigger('ui::lightbox_open', ['/api/v1/uploads/{{ upload }}']) {%- endif %}")) (text "{% endfor %}")) (text "{%- endif %} {%- endmacro %} {% macro notification(notification) -%}") (div @@ -623,7 +624,7 @@ (div ("class" "flex items_center") (b - (text "{{ self::username(user=user) }}")) + (text "{{ self::full_username(user=user, max_width=\"calc(100% - 42px - var(--pad-4))\") }}")) (text "{{ self::online_indicator(user=user) }}")))) (text "{%- endmacro %} {% macro pagination(page=0, items=0, key=\"\", value=\"\") -%}") diff --git a/crates/app/src/public/html/economy/edit.lisp b/crates/app/src/public/html/economy/edit.lisp index 175b566..28e5db5 100644 --- a/crates/app/src/public/html/economy/edit.lisp +++ b/crates/app/src/public/html/economy/edit.lisp @@ -4,6 +4,23 @@ (text "{% endblock %} {% block body %} {{ macros::nav(selected=\"\") }}") (main ("class" "flex flex_col gap_2") + (div + ("class" "card_nest") + (div + ("class" "card small flex items_center gap_2") + (icon (text "images")) + (b + (str (text "economy:label.thumbnails")))) + (div + ("class" "card flex flex_col gap_2") + (text "{{ components::post_media(upload_ids=product.uploads.thumbnails, custom_click=\"remove_thumbnail(event.target)\") }}") + (text "{% if product.uploads.thumbnails|length < 4 -%}") + (button + ("onclick" "add_thumbnail()") + (icon (text "plus")) + (str (text "communities:label.upload"))) + (text "{%- endif %}"))) + (div ("class" "card_nest") (div @@ -220,7 +237,88 @@ (str (text "general:action.delete"))))) (script - (text "async function update_title_from_form(e) { + (text "async function add_thumbnail() { + await trigger(\"atto::debounce\", [\"products::update\"]); + + const picker = document.createElement(\"input\"); + picker.type = \"file\"; + picker.accept = \"image/*\"; + document.body.appendChild(picker); + picker.click(); + + picker.addEventListener(\"change\", () => { + // create body + const body = new FormData(); + + for (const file of picker.files) { + body.append(file.name, file); + } + + body.append( + \"body\", + JSON.stringify({ + target: \"Thumbnails\" + }), + ); + + // ... + picker.remove(); + fetch(\"/api/v1/products/{{ product.id }}/uploads\", { + method: \"POST\", + body, + }) + .then((res) => res.json()) + .then(async (res) => { + trigger(\"atto::toast\", [ + res.ok ? \"success\" : \"error\", + res.message, + ]); + + if (res.ok) { + setTimeout(() => { + window.location.reload(); + }, 100); + } + }); + }); + } + + async function remove_thumbnail(target) { + await trigger(\"atto::debounce\", [\"products::update\"]); + + if ( + !(await trigger(\"atto::confirm\", [ + \"Are you sure you would like to do this?\", + ])) + ) { + return; + } + + fetch(\"/api/v1/products/{{ product.id }}/uploads/thumbnails\", { + method: \"DELETE\", + headers: { + \"Content-Type\": \"application/json\", + }, + body: JSON.stringify({ + idx: Array.from(target.parentElement.children).findIndex((x) => x.getAttribute(\"data-upload-id\") === target.getAttribute(\"data-upload-id\")), + }), + }) + .then((res) => res.json()) + .then(async (res) => { + trigger(\"atto::toast\", [ + res.ok ? \"success\" : \"error\", + res.message, + ]); + + if (res.ok) { + setTimeout(() => { + window.location.reload(); + }, 100); + } + }); + } + + async function update_title_from_form(e) { e.preventDefault(); await trigger(\"atto::debounce\", [\"products::update\"]); diff --git a/crates/app/src/public/html/economy/product.lisp b/crates/app/src/public/html/economy/product.lisp index d671551..b59e4fb 100644 --- a/crates/app/src/public/html/economy/product.lisp +++ b/crates/app/src/public/html/economy/product.lisp @@ -4,6 +4,7 @@ (text "{% endblock %} {% block body %} {{ macros::nav(selected=\"\") }}") (main ("class" "flex flex_col gap_2") + (text "{{ components::post_media(upload_ids=product.uploads.thumbnails) }}") (div ("class" "card flex flex_col gap_2") (h3