From 9872d3aacecffd0c5773dd12c2f6b2d78aaeaead Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Tue, 31 Oct 2023 23:32:36 +0100 Subject: [PATCH] Implement reaction create/destroy with Turbo Streams --- .../stylesheets/components/_answerbox.scss | 13 ++--- app/controllers/reactions_controller.rb | 56 +++++++++++++++++++ app/views/answerbox/_actions.html.haml | 7 ++- app/views/answerbox/_comments.html.haml | 7 ++- app/views/reactions/_create.html.haml | 13 +++++ app/views/reactions/_destroy.html.haml | 15 +++++ config/routes.rb | 4 ++ 7 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 app/views/reactions/_create.html.haml create mode 100644 app/views/reactions/_destroy.html.haml diff --git a/app/assets/stylesheets/components/_answerbox.scss b/app/assets/stylesheets/components/_answerbox.scss index ed550dc6..68065498 100644 --- a/app/assets/stylesheets/components/_answerbox.scss +++ b/app/assets/stylesheets/components/_answerbox.scss @@ -50,20 +50,19 @@ text-decoration: none; } - &[name="ab-smile"], - &[name="ab-smile-comment"] { + &.smile { color: var(--primary); &:hover { color: var(--success); } + } - &[data-action="unsmile"] { - color: var(--success); + &.unsmile { + color: var(--success); - &:hover { - color: var(--danger); - } + &:hover { + color: var(--danger); } } } diff --git a/app/controllers/reactions_controller.rb b/app/controllers/reactions_controller.rb index fbf01fce..350df903 100644 --- a/app/controllers/reactions_controller.rb +++ b/app/controllers/reactions_controller.rb @@ -1,9 +1,65 @@ # frozen_string_literal: true class ReactionsController < ApplicationController + include TurboStreamable + + before_action :authenticate_user!, only: %w[create destroy] + + turbo_stream_actions :create, :destroy + def index answer = Answer.includes([smiles: { user: :profile }]).find(params[:id]) render "index", locals: { a: answer } end + + def create + params.require :id + params.require :type + + target = params[:type].constantize.find_by!(id: params[:id]) + + UseCase::Reaction::Create.call( + source_user: current_user, + target: + ) + + target.reload + + respond_to do |format| + format.turbo_stream do + render turbo_stream: [ + turbo_stream.replace("reaction-#{params[:type]}-#{params[:id]}", partial: "reactions/destroy", locals: { type: params[:type], target: }), + render_toast(t(".#{params[:type].downcase}.success")) + ] + end + + format.html { redirect_back(fallback_location: root_path) } + end + end + + def destroy + params.require :id + params.require :type + + target = params[:type].constantize.find_by!(id: params[:id]) + + UseCase::Reaction::Destroy.call( + source_user: current_user, + target: + ) + + target.reload + + respond_to do |format| + format.turbo_stream do + render turbo_stream: [ + turbo_stream.replace("reaction-#{params[:type]}-#{params[:id]}", partial: "reactions/create", locals: { type: params[:type], target: }), + render_toast(t(".#{params[:type].downcase}.success")) + ] + end + + format.html { redirect_back(fallback_location: root_path) } + end + end end diff --git a/app/views/answerbox/_actions.html.haml b/app/views/answerbox/_actions.html.haml index 6dafce38..4842362d 100644 --- a/app/views/answerbox/_actions.html.haml +++ b/app/views/answerbox/_actions.html.haml @@ -1,6 +1,7 @@ -%button.btn.btn-link.answerbox__action{ type: :button, name: "ab-smile", data: { a_id: a.id, action: a.has_reacted ? :unsmile : :smile, selection_hotkey: "s" }, disabled: !user_signed_in? } - %i.fa.fa-fw.fa-smile-o - %span{ id: "ab-smile-count-#{a.id}" }= a.smile_count +- if a.has_reacted + = render "reactions/destroy", type: "Answer", target: a +- else + = render "reactions/create", type: "Answer", target: a - unless display_all %button.btn.btn-link.answerbox__action{ type: :button, name: "ab-comments", data: { a_id: a.id, state: :hidden, selection_hotkey: "x" } } %i.fa.fa-fw.fa-comments diff --git a/app/views/answerbox/_comments.html.haml b/app/views/answerbox/_comments.html.haml index 78ce559b..d992c946 100644 --- a/app/views/answerbox/_comments.html.haml +++ b/app/views/answerbox/_comments.html.haml @@ -16,9 +16,10 @@ .comment__content = markdown comment.content .flex-shrink-0.ms-auto - %button.btn.btn-link.answerbox__action{ type: :button, name: "ab-smile-comment", data: { c_id: comment.id, action: current_user&.smiled?(comment) ? :unsmile : :smile }, disabled: !user_signed_in? } - %i.fa.fa-fw.fa-smile-o - %span{ id: "ab-comment-smile-count-#{comment.id}" }= comment.smile_count + - if current_user&.smiled?(comment) + = render "reactions/destroy", type: "Comment", target: comment + - else + = render "reactions/create", type: "Comment", target: comment .btn-group %button.btn.btn-link.btn-sm.dropdown-toggle{ data: { bs_toggle: :dropdown }, aria: { expanded: false } } %span.caret diff --git a/app/views/reactions/_create.html.haml b/app/views/reactions/_create.html.haml new file mode 100644 index 00000000..a82c1d60 --- /dev/null +++ b/app/views/reactions/_create.html.haml @@ -0,0 +1,13 @@ +- if type == "Answer" + = button_to create_reactions_path(id: target.id, username: target.user.screen_name), + form: { class: "d-inline-block", id: "reaction-#{type}-#{target.id}" }, + class: "btn btn-link answerbox__action smile" do + %i.fa.fa-fw.fa-smile-o + %span= target.smile_count + +- if type == "Comment" + = button_to create_comment_reactions_path(id: target.id, username: target.user.screen_name), + form: { class: "d-inline-block", id: "reaction-#{type}-#{target.id}" }, + class: "btn btn-link answerbox__action smile" do + %i.fa.fa-fw.fa-smile-o + %span= target.smile_count diff --git a/app/views/reactions/_destroy.html.haml b/app/views/reactions/_destroy.html.haml new file mode 100644 index 00000000..23dffdaf --- /dev/null +++ b/app/views/reactions/_destroy.html.haml @@ -0,0 +1,15 @@ +- if type == "Answer" + = button_to destroy_reactions_path(id: target.id, username: target.user.screen_name), + method: :delete, + form: { class: "d-inline-block", id: "reaction-#{type}-#{target.id}" }, + class: "btn btn-link answerbox__action unsmile" do + %i.fa.fa-fw.fa-smile-o + %span= target.smile_count + +- if type == "Comment" + = button_to destroy_comment_reactions_path(id: target.id, username: target.user.screen_name), + method: :delete, + form: { class: "d-inline-block", id: "reaction-#{type}-#{target.id}" }, + class: "btn btn-link answerbox__action unsmile" do + %i.fa.fa-fw.fa-smile-o + %span= target.smile_count diff --git a/config/routes.rb b/config/routes.rb index fabc4263..28556dac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -156,8 +156,12 @@ Rails.application.routes.draw do delete "/@:username/a/:id/pin", to: "answer#unpin", as: :unpin_answer get "/@:username/a/:id/comments", to: "comments#index", as: :comments get "/@:username/a/:id/reactions", to: "reactions#index", as: :reactions + post "/@:username/a/:id/reactions", to: "reactions#create", as: :create_reactions, defaults: { type: "Answer" } + delete "/@:username/a/:id/reactions", to: "reactions#destroy", as: :destroy_reactions, defaults: { type: "Answer" } get "/@:username/q/:id", to: "question#show", as: :question get "/@:username/c/:id/reactions", to: "comments/reactions#index", as: :comment_reactions + post "/@:username/c/:id/reactions", to: "reactions#create", as: :create_comment_reactions, defaults: { type: "Comment" } + delete "/@:username/c/:id/reactions", to: "reactions#destroy", as: :destroy_comment_reactions, defaults: { type: "Comment" } get "/@:username/followers", to: "user#followers", as: :show_user_followers get "/@:username/followings", to: "user#followings", as: :show_user_followings get "/@:username/friends", to: redirect("/@%{username}/followings")