Merge pull request #5481 from consuldemocracy/remove_initialjs

Replace initialjs-rails with custom avatar code
This commit is contained in:
Javi Martín
2024-04-11 19:07:12 +02:00
committed by GitHub
21 changed files with 192 additions and 66 deletions

View File

@@ -30,7 +30,6 @@ gem "graphiql-rails", "~> 1.8.0"
gem "graphql", "~> 1.13.22"
gem "groupdate", "~> 6.4.0"
gem "image_processing", "~> 1.12.2"
gem "initialjs-rails", "~> 0.2.0.9"
gem "invisible_captcha", "~> 2.3.0"
gem "kaminari", "~> 1.2.2"
gem "mini_magick", "~> 4.12.0"

View File

@@ -280,8 +280,6 @@ GEM
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
initialjs-rails (0.2.0.9)
railties (>= 3.1, < 7.0)
invisible_captcha (2.3.0)
rails (>= 5.2)
json (2.7.1)
@@ -719,7 +717,6 @@ DEPENDENCIES
groupdate (~> 6.4.0)
i18n-tasks (~> 0.9.37)
image_processing (~> 1.12.2)
initialjs-rails (~> 0.2.0.9)
invisible_captcha (~> 2.3.0)
kaminari (~> 1.2.2)
knapsack_pro (~> 7.0.1)

View File

@@ -58,7 +58,6 @@
//= require ckeditor/loader
//= require_directory ./ckeditor
//= require social-share-button
//= require initial
//= require ahoy
//= require app
//= require check_all_none
@@ -75,7 +74,6 @@
//= require annotator
//= require jquery.amsify.suggestags
//= require tags
//= require users
//= require participation_not_allowed
//= require advanced_search
//= require registration_form
@@ -136,7 +134,6 @@ var initialize_modules = function() {
App.Answers.initialize();
App.Questions.initialize();
App.Comments.initialize();
App.Users.initialize();
App.ParticipationNotAllowed.initialize();
App.Tags.initialize();
App.FoundationExtras.initialize();

View File

@@ -1,15 +0,0 @@
(function() {
"use strict";
App.Users = {
initialize: function() {
var observer;
$(".initialjs-avatar").initial();
observer = new MutationObserver(function(mutations) {
$.each(mutations, function(index, mutation) {
$(mutation.addedNodes).find(".initialjs-avatar").initial();
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
};
}).call(this);

View File

@@ -25,6 +25,7 @@
@import "advanced_search";
@import "annotator_overrides";
@import "autocomplete_overrides";
@import "avatar";
@import "banner";
@import "comments_count";
@import "datepicker_overrides";
@@ -48,6 +49,7 @@
@import "account/**/*";
@import "admin/**/*";
@import "budgets/**/*";
@import "comments/**/*";
@import "debates/**/*";
@import "documents/**/*";
@import "layout/**/*";

View File

@@ -0,0 +1,4 @@
.initialjs-avatar {
font-family: HelveticaNeue-Light, "Helvetica Neue Light", "Helvetica Neue",
Helvetica, Arial, "Lucida Grande", sans-serif;
}

View File

@@ -0,0 +1,6 @@
.comment-avatar {
img,
svg {
float: $global-left;
}
}

View File

@@ -1558,8 +1558,9 @@ table {
.comment-body,
.notification-body {
img {
margin-right: calc(#{$line-height} / 2);
img,
svg {
margin-#{$global-right}: calc(#{$line-height} / 2);
}
.reply {

View File

@@ -0,0 +1,3 @@
<span class="comment-avatar">
<%= avatar %>
</span>

View File

@@ -0,0 +1,27 @@
class Comments::AvatarComponent < ApplicationComponent
attr_reader :comment
def initialize(comment)
@comment = comment
end
private
def avatar
if comment.as_administrator?
special_avatar("avatar_admin.png", class: "admin-avatar")
elsif comment.as_moderator?
special_avatar("avatar_moderator.png", class: "moderator-avatar")
elsif comment.user.hidden? || comment.user.erased?
tag.span(class: "icon-deleted user-deleted")
elsif comment.user.organization?
special_avatar("avatar_collective.png", class: "avatar")
else
render Shared::AvatarComponent.new(comment.user, size: 32)
end
end
def special_avatar(image_name, options = {})
image_tag(image_name, { size: 32, alt: "" }.merge(options))
end
end

View File

@@ -1 +1 @@
<%= avatar_image(record, options) %>
<%= avatar_image %>

View File

@@ -1,6 +1,5 @@
class Shared::AvatarComponent < ApplicationComponent
attr_reader :record, :given_options
use_helpers :avatar_image
def initialize(record, **given_options)
@record = record
@@ -10,7 +9,7 @@ class Shared::AvatarComponent < ApplicationComponent
private
def default_options
{ background_color: colors[seed % colors.size] }
{ background_color: colors[seed % colors.size], size: 100, color: "white" }
end
def options
@@ -27,4 +26,53 @@ class Shared::AvatarComponent < ApplicationComponent
def seed
record.id
end
def size
options[:size]
end
def font_size
(size * 0.6).round
end
def background_color
options[:background_color]
end
def color
options[:color]
end
def svg_options
{
xmlns: "http://www.w3.org/2000/svg",
width: size,
height: size,
role: "img",
"aria-label": "",
style: "background-color: #{background_color}",
class: "initialjs-avatar #{options[:class]}".strip
}
end
def text_options
{
x: "50%",
y: "50%",
dy: "0.35em",
"text-anchor": "middle",
fill: color,
style: "font-size: #{font_size}px"
}
end
def initial
record.name.first.upcase
end
def avatar_image
tag.svg(**svg_options) do
tag.text(initial, **text_options)
end
end
end

View File

@@ -9,19 +9,7 @@
</div>
<% end %>
<% else %>
<% if comment.as_administrator? %>
<%= image_tag("avatar_admin.png", size: 32, class: "admin-avatar float-left") %>
<% elsif comment.as_moderator? %>
<%= image_tag("avatar_moderator.png", size: 32, class: "moderator-avatar float-left") %>
<% else %>
<% if comment.user.hidden? || comment.user.erased? %>
<span class="icon-deleted user-deleted"></span>
<% elsif comment.user.organization? %>
<%= image_tag("avatar_collective.png", size: 32, class: "avatar float-left") %>
<% else %>
<%= render Shared::AvatarComponent.new(comment.user, size: 32, class: "float-left") %>
<% end %>
<% end %>
<%= render Comments::AvatarComponent.new(comment) %>
<div class="comment-info">

View File

@@ -0,0 +1,56 @@
require "rails_helper"
describe Comments::AvatarComponent do
it "displays a regular avatar for regular comments" do
comment = create(:comment, user: create(:user, username: "Oscar Wilde"))
render_inline Comments::AvatarComponent.new(comment)
expect(page).to have_avatar "O"
expect(page).not_to have_css "img"
end
it "displays the admin avatar with an empty alt attribute for admin comments" do
admin = create(:administrator)
comment = create(:comment, user: admin.user, administrator_id: admin.id)
render_inline Comments::AvatarComponent.new(comment)
expect(page).to have_css "img.admin-avatar[alt='']"
end
it "displays the moderator avatar with an empty alt attribute for moderator comments" do
moderator = create(:moderator)
comment = create(:comment, user: moderator.user, moderator_id: moderator.id)
render_inline Comments::AvatarComponent.new(comment)
expect(page).to have_css "img.moderator-avatar[alt='']"
end
it "displays the organization avatar with an empty alt attribute for organization comments" do
comment = create(:comment, user: create(:organization).user)
render_inline Comments::AvatarComponent.new(comment)
expect(page).to have_css "img.avatar[alt='']"
end
it "displays an empty icon for comments by hidden users" do
comment = create(:comment, user: create(:user, :hidden))
render_inline Comments::AvatarComponent.new(comment)
expect(page).to have_css ".user-deleted"
expect(page).not_to have_css "img"
end
it "displays an empty icon for comments by erased users" do
comment = create(:comment, user: create(:user, erased_at: Time.current))
render_inline Comments::AvatarComponent.new(comment)
expect(page).to have_css ".user-deleted"
expect(page).not_to have_css "img"
end
end

View File

@@ -0,0 +1,22 @@
require "rails_helper"
describe Shared::AvatarComponent do
let(:user) { double(id: 1, name: "Johnny") }
let(:component) { Shared::AvatarComponent.new(user) }
it "does not contain redundant text already present around it" do
render_inline component
expect(page).to have_css "svg", count: 1
expect(page).to have_css "svg[role='img'][aria-label='']"
end
it "shows the initial letter of the name" do
render_inline component
page.find("svg") do |avatar|
expect(avatar).to have_text "J"
expect(avatar).not_to have_text "Johnny"
end
end
end

View File

@@ -18,8 +18,4 @@ module Comments
end
expect(page).to have_content "It will be done next week."
end
def avatar(name)
"img.initialjs-avatar[data-name='#{name}']"
end
end

View File

@@ -0,0 +1,9 @@
RSpec::Matchers.define :have_avatar do |text, **options|
match do |page|
page.has_css?("svg.initialjs-avatar", **{ exact_text: text }.merge(options))
end
failure_message do
"expected to find avatar with text #{text} but there were no matches."
end
end

View File

@@ -15,7 +15,7 @@ describe "Account" do
expect(page).to have_current_path(account_path, ignore_query: true)
expect(page).to have_css "input[value='Manuela Colau']"
expect(page).to have_css avatar("Manuela Colau"), count: 1
expect(page).to have_avatar "M", count: 1
end
scenario "Show organization" do
@@ -26,7 +26,7 @@ describe "Account" do
expect(page).to have_css "input[value='Manuela Corp']"
expect(page).not_to have_css "input[value='Manuela Colau']"
expect(page).to have_css avatar("Manuela Corp"), count: 1
expect(page).to have_avatar "M", count: 1
end
scenario "Edit" do

View File

@@ -72,15 +72,15 @@ describe "Debates" do
end
scenario "Show" do
debate = create(:debate)
debate = create(:debate, author: create(:user, username: "Charles Dickens"))
visit debate_path(debate)
expect(page).to have_content debate.title
expect(page).to have_content "Debate description"
expect(page).to have_content debate.author.name
expect(page).to have_content "Charles Dickens"
expect(page).to have_content I18n.l(debate.created_at.to_date)
expect(page).to have_css avatar(debate.author.name)
expect(page).to have_avatar "C"
expect(page.html).to include "<title>#{debate.title}</title>"
end

View File

@@ -114,16 +114,16 @@ describe "Proposals" do
end
scenario "Show" do
proposal = create(:proposal)
proposal = create(:proposal, author: create(:user, username: "Mark Twain"))
visit proposal_path(proposal)
expect(page).to have_content proposal.title
expect(page).to have_content proposal.code
expect(page).to have_content "Proposal description"
expect(page).to have_content proposal.author.name
expect(page).to have_content "Mark Twain"
expect(page).to have_content I18n.l(proposal.created_at.to_date)
expect(page).to have_css avatar(proposal.author.name)
expect(page).to have_avatar "M"
expect(page.html).to include "<title>#{proposal.title}</title>"
expect(page).not_to have_css ".js-flag-actions"
expect(page).not_to have_css ".js-follow"

View File

@@ -485,18 +485,4 @@ describe "Users" do
expect(page).not_to have_content("Sport")
end
end
describe "Initials" do
scenario "display SVG avatars when loaded into the DOM" do
login_as(create(:user))
visit debate_path(create(:debate))
fill_in "Leave your comment", with: "I'm awesome"
click_button "Publish comment"
within ".comment", text: "I'm awesome" do
expect(page).to have_css "img.initialjs-avatar[src^='data:image/svg']"
end
end
end
end