ai tags: add buttons for quickly adding and removing tags on the /ai_tags page.

Add "Add" and "Remove" buttons beneath thumbnails on the /ai_tags page.
These let you add the tag to the post if it's correct, or remove it if
it's wrong.
This commit is contained in:
evazion
2022-06-26 00:34:50 -05:00
parent 7b1f6e42c1
commit e5879f0def
10 changed files with 92 additions and 11 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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; }

View File

@@ -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/

View File

@@ -4,4 +4,8 @@ class AITagPolicy < ApplicationPolicy
def index?
true
end
def tag?
unbanned? && record.post.present? && policy(record.post).update?
end
end

View File

@@ -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 %>
<div class="text-center text-xs h-8">
<% if media_asset.post.present? %>
<%= link_to "post ##{media_asset.post.id}", media_asset.post %>
<% end %>
<div class="text-center text-xs flex flex-col px-2 h-full justify-end">
<div class="mb-2">
<% if media_asset.post.present? %>
<div>
<%= 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)) %>
</div>
<% end %>
<div>
<%= 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 %>
<div class="leading-none">
<%= 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 %>
</div>
</div>
<% 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 %>
</div>
<% end %>
<% end %>

View File

@@ -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 %>

View File

@@ -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] %>");
});

View File

@@ -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]