Skip to content

Commit

Permalink
Merge pull request #16789 from opf/chore/primerized-flashes
Browse files Browse the repository at this point in the history
Primerize all backend flashes
  • Loading branch information
oliverguenther authored Sep 30, 2024
2 parents 9c82909 + cd2d0f0 commit 87ffaff
Show file tree
Hide file tree
Showing 121 changed files with 535 additions and 651 deletions.
6 changes: 5 additions & 1 deletion app/components/op_primer/flash_component.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ See COPYRIGHT and LICENSE files for more details.
render(Primer::BaseComponent.new(
tag: :div,
classes: "op-primer-flash--item",
data: { unique_key: @unique_key }.compact
data: {
"flash-target": "item",
"autohide": @autohide,
unique_key: @unique_key
}.compact
)) do
render_parent
end
Expand Down
4 changes: 3 additions & 1 deletion app/components/op_primer/flash_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ class FlashComponent < Primer::Alpha::Banner
def initialize(**system_arguments)
@unique_key = system_arguments.delete(:unique_key)

system_arguments[:test_selector] ||= "primer-banner-message-component"
system_arguments[:test_selector] ||= "op-primer-flash-message"
system_arguments[:dismiss_scheme] ||= :remove
system_arguments[:dismiss_label] ||= I18n.t(:button_close)

@autohide = system_arguments[:scheme] == :success && system_arguments[:dismiss_scheme] != :none

super
end
end
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/my_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,13 @@ def revoke_api_key

# rubocop:disable Rails/ActionControllerFlashBeforeRender
result.on_success do
flash[:op_primer_flash] = { message: t("my.access_token.notice_api_token_revoked") }
flash[:notice] = t("my.access_token.notice_api_token_revoked")
end

result.on_failure do |r|
error = r.errors.map(&:message).join("; ")
Rails.logger.error("Failed to revoke api token ##{current_user.id}: #{error}")
flash[:op_primer_flash] = { message: t("my.access_token.failed_to_revoke_token", error:), scheme: :danger }
flash[:error] = t("my.access_token.failed_to_revoke_token", error:)
end
# rubocop:enable Rails/ActionControllerFlashBeforeRender

Expand Down
39 changes: 29 additions & 10 deletions app/helpers/error_message_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,51 @@ def error_messages_for(object)
object = convert_to_model(object)
return unless object

render_error_messages_partial(object.errors, object)
assign_flash_error(object.errors, object)

# Don't output anything for compability
nil
end

def render_error_messages_partial(errors, object)
return "" if errors.empty?
def assign_flash_error(errors, object)
return if errors.empty?

base_error_messages = errors.full_messages_for(:base)
fields_error_messages = errors.full_messages - base_error_messages

render partial: "common/validation_error",
locals: { base_error_messages:,
fields_error_messages:,
object_name: object.class.model_name.human }
flash[:error] = {
message: error_message_header(object.class.model_name.human, base_error_messages.count + fields_error_messages.count),
description: error_flash_description(object, base_error_messages, fields_error_messages)
}
end

def error_flash_description(_object, base_error_messages, fields_error_messages)
capture do
concat list_of_messages(base_error_messages)
concat text_header_invalid_fields(base_error_messages, fields_error_messages)
concat list_of_messages(fields_error_messages)
end
end

def error_message_header(object_name, count)
t("activerecord.errors.template.header", model: object_name, count:)
end

def text_header_invalid_fields(base_error_messages, fields_error_messages)
return if fields_error_messages.blank?

i18n_key = base_error_messages.present? ? "errors.header_additional_invalid_fields" : "errors.header_invalid_fields"
t(i18n_key, count: fields_error_messages.count)
out = "".html_safe

out << t(i18n_key, count: fields_error_messages.count)
out << "<br/>".html_safe

out
end

def list_of_messages(messages)
return if messages.blank?

messages = messages.map { |message| tag.li message }
tag.ul { safe_join(messages, "\n") }
safe_join(messages, "<br/>".html_safe)
end
end
51 changes: 24 additions & 27 deletions app/helpers/errors_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,34 @@
#++

module ErrorsHelper
def render_400(options = {})
@project = nil
def render_400(options = {}) # rubocop:disable Naming/VariableNumber
unset_template_magic
render_error({ message: :notice_bad_request, status: 400 }.merge(options))
false
end

def render_403(options = {})
@project = nil
def render_403(options = {}) # rubocop:disable Naming/VariableNumber
unset_template_magic
render_error({ message: :notice_not_authorized, status: 403 }.merge(options))
false
end

def render_404(options = {})
def render_404(options = {}) # rubocop:disable Naming/VariableNumber
render_error({ message: :notice_file_not_found, status: 404 }.merge(options))
false
end

def render_500(options = {})
message = t(:notice_internal_server_error, app_title: Setting.app_title)

def render_500(options = {}) # rubocop:disable Naming/VariableNumber
unset_template_magic

message = t(:notice_internal_server_error, app_title: Setting.app_title)

# Append error information
if current_user.admin?
options[:message_details] = get_additional_message
end

render_error({ message: }.merge(options))
render_error({ message:, status: 500 }.merge(options))
false
end

Expand All @@ -80,37 +80,34 @@ def render_optional_error_file(status_code)
end

# Renders an error response
def render_error(arg)
def render_error(arg) # rubocop:disable Metrics/AbcSize
arg = { message: arg } unless arg.is_a?(Hash)

@status = arg[:status] || 500
@message = arg[:message]
status = arg[:status] || 500
message = arg[:message]

if @status >= 500
op_handle_error(arg[:exception] || "[Error #@status] #@message", payload: arg[:payload])
if status >= 500
op_handle_error(arg[:exception] || "[Error #status] #message", payload: arg[:payload])
end

@message = I18n.t(@message) if @message.is_a?(Symbol)
@message_details = arg[:message_details]
message = I18n.t(message) if message.is_a?(Symbol)
message_details = arg[:message_details]
respond_to do |format|
format.html do
render template: "common/error", layout: use_layout, status: @status
error_message = "[#{I18n.t(:error_code, code: status)}] #{message}\n#{message_details}"
flash.now[:error] = { message: error_message, dismiss_scheme: :none }
render template: "common/error",
layout: use_layout,
status:,
locals: { status:, params: }
end
format.any do
head @status
head status
end
end
end

def unset_template_magic
if $ERROR_INFO.is_a?(ActionView::ActionViewError)
@template.instance_variable_set(:@project, nil)
@template.instance_variable_set(:@status, 500)
@template.instance_variable_set(:@message, message)
else
@project = nil
end
rescue StandardError
# bad luck
@project = nil # rubocop:disable Rails/HelperInstanceVariable
end
end
58 changes: 30 additions & 28 deletions app/helpers/flash_messages_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,44 +37,46 @@ module FlashMessagesHelper
# Renders flash messages
def render_flash_messages
messages = flash
.reject { |k, _| k.to_s == "op_primer_flash" }
.reject { |k, _| k.start_with? "_" }
.map do |k, v|
if k.to_sym == :modal
component = v[:type].constantize
component.new(**v.fetch(:parameters, {})).render_in(self)
else
render_flash_message(k, v)
end
end
.reject { |k, _| k.to_s == "modal" }
.map { |k, v| render_flash_content(k.to_sym, v) }

safe_join messages, "\n"
end

def render_flash_message(type, message, html_options = {}) # rubocop:disable Metrics/AbcSize
if type.to_s == "notice"
type = "success"
def render_flash_content(key, content)
case content
when Hash
render_flash_message(key, **content)
else
render_flash_message(key, message: content)
end
end

toast_css_classes = ["op-toast -#{type}", html_options.delete(:class)]
def render_flash_modal
content = flash[:modal]
return if content.blank?

# Add autohide class to notice flashes if configured
if type.to_s == "success" && User.current.pref.auto_hide_popups?
toast_css_classes << "autohide-toaster"
component = content[:type].constantize
component.new(**content.fetch(:parameters, {})).render_in(self)
end

def mapped_flash_type(type)
case type
when :error, :danger
:danger
when :warning
:warning
when :success, :notice
:success
else
:default
end
end

html_options = { class: toast_css_classes.join(" "), role: "alert" }.merge(html_options)
close_button = content_tag :a, "", class: "op-toast--close icon-context icon-close",
title: I18n.t("js.close_popup_title"),
tabindex: "0"
toast = content_tag(:div, join_flash_messages(message), class: "op-toast--content")
content_tag :div, "", class: "op-toast--wrapper" do
content_tag :div, "", class: "op-toast--casing" do
content_tag :div, html_options do
concat(close_button)
concat(toast)
end
end
def render_flash_message(type, message:, **args)
render(OpPrimer::FlashComponent.new(scheme: mapped_flash_type(type), **args)) do
join_flash_messages(message)
end
end
end
43 changes: 0 additions & 43 deletions app/helpers/primerized_flash_helper.rb

This file was deleted.

53 changes: 0 additions & 53 deletions app/views/common/_error_base.html.erb

This file was deleted.

Loading

0 comments on commit 87ffaff

Please sign in to comment.