diff --git a/app/controllers/ai_tags_controller.rb b/app/controllers/ai_tags_controller.rb index ed028dcdb..b29c0460c 100644 --- a/app/controllers/ai_tags_controller.rb +++ b/app/controllers/ai_tags_controller.rb @@ -12,4 +12,26 @@ class AITagsController < ApplicationController respond_with(@ai_tags) end + + # Add the tag to the post, or remove the tag from the post. + def tag + @ai_tag = authorize AITag.find_by!(media_asset_id: params[:media_asset_id], tag_id: params[:tag_id]) + @post = @ai_tag.post + + if params[:mode] == "remove" + @post.remove_tag(@ai_tag.tag.name) + flash.now[:notice] = DText.format_text("Post ##{@post.id}: Removed [[#{@ai_tag.tag.pretty_name}]].", inline: true).html_safe + else + @post.add_tag(@ai_tag.tag.name) + flash.now[:notice] = DText.format_text("Post ##{@post.id}: Added [[#{@ai_tag.tag.pretty_name}]].", inline: true).html_safe + end + + @post.save + if @post.invalid? + flash.now[:notice] = DText.format_text("Couldn't update post ##{@post.id}: #{@post.errors.full_messages.join("; ")}", inline: true).html_safe + end + + @preview_size = params[:size].presence || cookies[:post_preview_size].presence || PostGalleryComponent::DEFAULT_SIZE + respond_with(@ai_tag) + end end diff --git a/app/javascript/src/styles/base/040_colors.scss b/app/javascript/src/styles/base/040_colors.scss index 23e5c9b30..5e474c343 100644 --- a/app/javascript/src/styles/base/040_colors.scss +++ b/app/javascript/src/styles/base/040_colors.scss @@ -142,7 +142,9 @@ html, body[data-current-user-theme="light"] { --button-primary-background-color: var(--link-color); --button-primary-hover-background-color: var(--link-hover-color); --button-primary-disabled-color: var(--grey-5); + --button-outline-primary-color: var(--link-color); + --button-outline-danger-color: var(--red-5); --quick-search-form-background: var(--body-background-color); @@ -360,7 +362,9 @@ html, body[data-current-user-theme="light"] { --button-primary-background-color: var(--link-color); --button-primary-hover-background-color: var(--link-hover-color); --button-primary-disabled-color: var(--grey-4); + --button-outline-primary-color: var(--azure-4); + --button-outline-danger-color: var(--red-4); --quick-search-form-background: var(--grey-9); diff --git a/app/javascript/src/styles/common/buttons.scss b/app/javascript/src/styles/common/buttons.scss index 4850815ab..8d6348683 100644 --- a/app/javascript/src/styles/common/buttons.scss +++ b/app/javascript/src/styles/common/buttons.scss @@ -84,6 +84,27 @@ a, button, input[type="submit"] { } } + /* An outlined red button. */ + &.button-outline-danger { + @extend %button; + + color: var(--button-outline-danger-color); + background-color: transparent; + border: 1px solid var(--button-outline-danger-color); + + transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + + &:hover:not([disabled]) { + color: var(--inverse-text-color); + background-color: var(--button-outline-danger-color); + } + + &[disabled] { + color: var(--button-primary-disabled-color); + border: 1px solid var(--button-primary-disabled-color); + } + } + /* A small button. */ &.button-sm { padding: 0.25em 1em; diff --git a/app/javascript/src/styles/common/utilities.scss b/app/javascript/src/styles/common/utilities.scss index 79686c1b5..3be0ac551 100644 --- a/app/javascript/src/styles/common/utilities.scss +++ b/app/javascript/src/styles/common/utilities.scss @@ -152,6 +152,8 @@ $spacer: 0.25rem; /* 4px */ .h-8 { height: 8 * $spacer; } .h-10 { height: 10 * $spacer; } .h-12 { height: 12 * $spacer; } +.h-16 { height: 16 * $spacer; } +.h-full { height: 100%; } .h-150px { height: 150px; } .h-180px { height: 180px; } .h-225px { height: 225px; } @@ -194,6 +196,10 @@ $spacer: 0.25rem; /* 4px */ .justify-items-center { justify-items: center; } .justify-center { justify-content: center; } +.justify-between { justify-content: space-between; } +.justify-end { justify-content: flex-end; } + +.self-start { align-self: flex-start; } .float-right { float: right; } diff --git a/app/models/ai_tag.rb b/app/models/ai_tag.rb index 90ad4ea97..ccf487704 100644 --- a/app/models/ai_tag.rb +++ b/app/models/ai_tag.rb @@ -40,7 +40,8 @@ class AITag < ApplicationRecord order(media_asset_id: :desc, tag_id: :asc) end - def correct? + # True if the AI tag is present on the post; false if the AI tag is not on the post, or the asset isn't a post yet. + def post_tagged? if post.nil? false elsif tag.name =~ /\Arating:(.)\z/ diff --git a/app/policies/ai_tag_policy.rb b/app/policies/ai_tag_policy.rb index debd7d4fd..6995e93d4 100644 --- a/app/policies/ai_tag_policy.rb +++ b/app/policies/ai_tag_policy.rb @@ -4,4 +4,8 @@ class AITagPolicy < ApplicationPolicy def index? true end + + def tag? + unbanned? && record.post.present? && policy(record.post).update? + end end diff --git a/app/views/ai_tags/_preview.html.erb b/app/views/ai_tags/_preview.html.erb index b4f08cdeb..b55eee7e4 100644 --- a/app/views/ai_tags/_preview.html.erb +++ b/app/views/ai_tags/_preview.html.erb @@ -1,14 +1,27 @@ -<%= render(MediaAssetPreviewComponent.new(media_asset: media_asset, size: size, link_target: media_asset.post || media_asset, html: { **data_attributes_for(media_asset) })) do |preview| %> +<%= render(MediaAssetPreviewComponent.new(media_asset: media_asset, size: size, link_target: media_asset.post || media_asset, classes: "flex flex-col items-center w-full h-full border rounded pb-2", html: { "data-tag-id": ai_tag.tag_id, **data_attributes_for(media_asset) })) do |preview| %> <% preview.footer do %> -
- <% if media_asset.post.present? %> - <%= link_to "post ##{media_asset.post.id}", media_asset.post %> - <% end %> +
+
+ <% if media_asset.post.present? %> +
+ <%= link_to "post ##{media_asset.post.id}", media_asset.post %> + <%= link_to "ยป", ai_tags_path(search: search_params.merge(media_asset_id: ai_tag.media_asset_id)) %> +
+ <% end %> -
- <%= link_to ai_tag.tag.pretty_name, ai_tags_path(search: { tag_name: ai_tag.tag.name, **params[:search].except(:tag_name) }), class: "tag-type-#{ai_tag.tag.category}", "data-tag-name": ai_tag.tag.name %> - <%= link_to "#{ai_tag.score}%", ai_tags_path(search: { tag_name: ai_tag.tag.name, score: ">=#{ai_tag.score}", **params[:search].except(:tag_name, :score) }), class: "tag-type-#{ai_tag.tag.category}", "data-tag-name": ai_tag.tag.name %> +
+ <%= link_to ai_tag.tag.pretty_name, ai_tags_path(search: search_params.merge(tag_name: ai_tag.tag.name)), class: "tag-type-#{ai_tag.tag.category}", "data-tag-name": ai_tag.tag.name %> + <%= link_to "#{ai_tag.score}%", ai_tags_path(search: search_params.merge(tag_name: ai_tag.tag.name, score: ">=#{ai_tag.score}")), class: "tag-type-#{ai_tag.tag.category}", "data-tag-name": ai_tag.tag.name %> +
+ + <% if ai_tag.post.nil? %> + <%= button_to "Add", nil, class: "button-primary button-sm", disabled: true %> + <% elsif ai_tag.post_tagged? %> + <%= button_to "Remove", tag_ai_tag_path(media_asset_id: ai_tag.media_asset, tag_id: ai_tag.tag), remote: true, method: :put, params: { mode: "remove" }, class: "button-outline-danger button-sm" %> + <% else %> + <%= button_to "Add", tag_ai_tag_path(media_asset_id: ai_tag.media_asset, tag_id: ai_tag.tag), remote: true, method: :put, params: { mode: "add" }, class: "button-primary button-sm" %> + <% end %>
<% end %> <% end %> diff --git a/app/views/ai_tags/_table.html.erb b/app/views/ai_tags/_table.html.erb index 42e33e0ce..e13b8d146 100644 --- a/app/views/ai_tags/_table.html.erb +++ b/app/views/ai_tags/_table.html.erb @@ -18,7 +18,7 @@ <%= ai_tag.score %>% <% end %> - <% t.column "Present?" do |ai_tag| %> - <%= "Yes" if ai_tag.correct? %> + <% t.column "Tagged?" do |ai_tag| %> + <%= "Yes" if ai_tag.post_tagged? %> <% end %> <% end %> diff --git a/app/views/ai_tags/tag.js.erb b/app/views/ai_tags/tag.js.erb new file mode 100644 index 000000000..bb6bdc63c --- /dev/null +++ b/app/views/ai_tags/tag.js.erb @@ -0,0 +1,7 @@ +$(function() { + let $el = $(".media-asset-preview[data-id=<%= @ai_tag.media_asset_id %>][data-tag-id=<%= @ai_tag.tag_id %>]").get(0); + let html = "<%= j render("ai_tags/preview", ai_tag: @ai_tag, media_asset: @ai_tag.media_asset, size: @preview_size) %>"; + morphdom($el, html); + + Danbooru.Utility.notice("<%= j flash[:notice] %>"); +}); diff --git a/config/routes.rb b/config/routes.rb index 0699f57d2..4a352a6fb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -158,7 +158,10 @@ Rails.application.routes.draw do end resources :media_assets, only: [:index, :show] resources :media_metadata, only: [:index] + resources :ai_tags, only: [:index] + put "/ai_tags/:media_asset_id/:tag_id/tag", to: "ai_tags#tag", as: "tag_ai_tag" + resources :mod_actions resources :moderation_reports, only: [:new, :create, :index, :show, :update] resources :modqueue, only: [:index]