Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install view_component and add initial AvatarComponent #1403

Merged
merged 6 commits into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Lint/NestedMethodDefinition:
Exclude:
- api/sinatra/**/*

Lint/MissingSuper:
Exclude:
- app/components/**/*


### Metrics

Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ gem "fake_email_validator"
# TLD validation
gem "tldv", "~> 0.1.0"

gem "view_component"

gem "jwt", "~> 2.7"

group :development do
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ GEM
unf_ext (0.0.8)
unicode-display_width (2.5.0)
uniform_notifier (1.16.0)
view_component (3.6.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
warden (1.2.9)
rack (>= 2.0.9)
webpush (1.1.0)
Expand Down Expand Up @@ -580,6 +584,7 @@ DEPENDENCIES
tldv (~> 0.1.0)
turbo-rails
twitter-text
view_component

BUNDLED WITH
2.4.21
4 changes: 4 additions & 0 deletions app/components/avatar_component.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
%img{ class: avatar_classes,
alt: alt_text,
src: avatar_image,
loading: :lazy }
30 changes: 30 additions & 0 deletions app/components/avatar_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

class AvatarComponent < ViewComponent::Base
ALLOWED_SIZES = %w[xs sm md lg xl xxl].freeze

def initialize(user:, size:, classes: [])
@user = user
@size = size if ALLOWED_SIZES.include? size
@classes = classes
end

private

def size_to_version(size)
case size
when "xs", "sm"
:small
when "md", "lg"
:medium
when "xl", "xxl"
:large
end
end

def alt_text = "@#{@user.screen_name}"

def avatar_classes = @classes.unshift("avatar-#{@size}")

def avatar_image = @user.profile_picture.url(size_to_version(@size))
end
2 changes: 1 addition & 1 deletion app/views/answerbox/_comments.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
.d-flex
.flex-shrink-0
%a{ href: user_path(comment.user) }
%img.comment__user-avatar.avatar-sm{ src: comment.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: comment.user, size: "sm", classes: ["comment__user-avatar"])
.flex-grow-1
%h6.comment__user
= user_screen_name comment.user
Expand Down
2 changes: 1 addition & 1 deletion app/views/answerbox/_header.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
- unless a.question.author_is_anonymous
.flex-shrink-0
%a{ href: user_path(a.question.user) }
%img.answerbox__question-user-avatar.avatar-md{ src: a.question.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: a.question.user, size: "md", classes: ["answerbox__question-user-avatar"])
.flex-grow-1
%h6.text-muted.answerbox__question-user
- if a.question.author_is_anonymous
Expand Down
2 changes: 1 addition & 1 deletion app/views/answerbox/_smiles.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
%a{ href: user_path(smile.user),
title: user_screen_name(smile.user, url: false),
data: { bs_toggle: :tooltip, bs_placement: :top, smile_id: smile.id } }
%img.avatar-xs{ src: smile.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: smile.user, size: "xs")
2 changes: 1 addition & 1 deletion app/views/application/_answerbox.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
.d-flex
.flex-shrink-0
%a{ href: user_path(a.user) }
%img.answerbox__answer-user-avatar.avatar-sm{ src: a.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: a.user, size: "sm", classes: ["answerbox__answer-user-avatar"])
.flex-grow-1
%h6.answerbox__answer-user
= raw t(".answered", hide: hidespan(t(".hide"), "d-none d-sm-inline"), user: user_screen_name(a.user))
Expand Down
2 changes: 1 addition & 1 deletion app/views/discover/_userbox.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
.d-flex
.flex-shrink-0
%a{ href: user_path(u) }
%img.avatar-md.me-2{ src: u.profile_picture.url(:medium) }
= render AvatarComponent.new(user: u, size: "md", classes: ["me-2"])
.flex-grow-1
%h6.answerbox__question-user
- if u.profile.display_name.blank?
Expand Down
2 changes: 1 addition & 1 deletion app/views/inbox/_entry.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- unless i.question.author_is_anonymous
.flex-shrink-0
%a.pull-left{ href: user_path(i.question.user) }
%img.answerbox__question-user-avatar.avatar-md{ src: i.question.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: i.question.user, size: "md", classes: ["answerbox__question-user-avatar"])
.flex-grow-1
%h6.text-muted.answerbox__question-user
- if i.question.author_is_anonymous
Expand Down
2 changes: 1 addition & 1 deletion app/views/moderation/_moderationbox.html.haml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.card.moderationbox{ data: { id: report.id } }
.card-header
%img.avatar-sm{ src: report.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: report.user, size: "sm")
= t(".reported_html",
user: user_screen_name(report.user),
content: report.type.sub("Reports::", ""),
Expand Down
2 changes: 1 addition & 1 deletion app/views/moderation/inbox/_header.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
.d-flex
.flex-shrink-0
%a{ href: user_path(user) }
%img.answerbox__question-user-avatar.avatar-md{ src: user.profile_picture.url(:medium) }
= render AvatarComponent.new(user:, size: "md", classes: ["answerbox__question-user-avatar"])
.flex-grow-1
= t(".title_html", screen_name: user.screen_name, user_id: user.id)
2 changes: 1 addition & 1 deletion app/views/navigation/_desktop.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
%i.fa.fa-pencil-square-o
%li.nav-item.dropdown.profile--image-dropdown
%a.nav-link.dropdown-toggle.p-sm-0{ href: "#", data: { bs_toggle: :dropdown } }
%img.avatar-md.d-none.d-sm-inline{ src: current_user.profile_picture.url(:small) }
= render AvatarComponent.new(user: current_user, size: "md", classes: ["d-none", "d-sm-inline"])
%span.d-inline.d-sm-none
= current_user.screen_name
%b.caret
Expand Down
2 changes: 1 addition & 1 deletion app/views/navigation/_mobile.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
badge: notification_count, badge_color: "primary", badge_attr: { id: "notification-mobile-count" }, icon_only: true
%li.nav-item.profile--image-dropdown
%a.nav-link{ href: '#', data: { bs_toggle: 'dropdown', bs_target: '#rs-mobile-nav-profile' }, aria: { controls: 'rs-mobile-nav-profile', expanded: 'false' } }
%img.avatar-md.d-inline{ src: current_user.profile_picture.url(:small) }
= render AvatarComponent.new(user: current_user, size: "md", classes: ["d-inline"])
= render 'navigation/dropdown/profile', size: "mobile"
2 changes: 1 addition & 1 deletion app/views/notifications/type/_answer.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%i.fa.fa-2x.fa-fw.fa-exclamation
.flex-grow-1
.notification__heading
%img.avatar-xs{ src: notification.target.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: notification.target.user, size: "xs")
= t(".heading_html",
user: user_screen_name(notification.target.user),
question: link_to(t(".link_text"), answer_path(username: notification.target.user.screen_name, id: notification.target.id), target: "_top"),
Expand Down
2 changes: 1 addition & 1 deletion app/views/notifications/type/_comment.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%i.fa.fa-2x.fa-fw.fa-comments
.flex-grow-1
.notification__heading
%img.avatar-xs{ src: notification.target.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: notification.target.user, size: "xs")
- if notification.target.answer.user == current_user
= t(".heading_html",
user: user_screen_name(notification.target.user),
Expand Down
2 changes: 1 addition & 1 deletion app/views/notifications/type/_follow.html.haml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.d-flex.notification
.flex-shrink-0.notification__icon
%img.avatar-sm{ src: notification.target.source.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: notification.target.source, size: "sm")
.flex-grow-1
%h6.notification__user
= user_screen_name notification.target.source
Expand Down
2 changes: 1 addition & 1 deletion app/views/notifications/type/_reaction.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
%i.fa.fa-2x.fa-fw.fa-smile-o
.flex-grow-1
.notification__heading
%img.avatar-xs{ src: notification.target.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: notification.target.user, size: "xs")
- if notification.target.parent_type == "Answer"
= t(".heading_html",
user: user_screen_name(notification.target.user),
Expand Down
2 changes: 1 addition & 1 deletion app/views/question/_question.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- unless question.author_is_anonymous
.flex-shrink-0
%a{ href: unless hidden then user_path(question.user) end }
%img.answerbox__question-user-avatar.avatar-md{ src: question.user.profile_picture.url(:small) }
= render AvatarComponent.new(user: question.user, size: "md", classes: ["answerbox__question-user-avatar"])
.flex-grow-1
%h6.text-muted.answerbox__question-user
- identifier = question.author_is_anonymous ? question.author_identifier : nil
Expand Down
2 changes: 1 addition & 1 deletion app/views/settings/blocks/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- @blocks.each do |block|
%li.list-group-item
.d-flex
%img.avatar-md.d-none.d-sm-inline.me-2{ src: block.target.profile_picture.url(:small) }
= render AvatarComponent.new(user: block.target, size: "md", classes: ["d-none", "d-sm-inline", "me-2"])
%div
%p.mb-0= user_screen_name(block.target)
%p.text-muted.mb-0= t(".blocked", time: time_ago_in_words(block.created_at))
Expand Down
2 changes: 1 addition & 1 deletion app/views/settings/mutes/_user.html.haml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.d-flex.mb-2
%img.avatar-md.me-2{ src: user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user:, size: "md", classes: ["me-2"])
%p.align-self-center.m-0= user_screen_name(user, context_user: current_user)
.ms-auto.d-inline-flex
%button.btn.btn-default.align-self-center{ data: { action: :unmute, target: user.screen_name } }
Expand Down
2 changes: 1 addition & 1 deletion app/views/settings/profile/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
%div{ data: { controller: "cropper", cropper_aspect_ratio_value: "1" } }
.d-flex
.flex-shrink-0
%img.avatar-lg.me-3{ src: current_user.profile_picture.url(:medium) }
= render AvatarComponent.new(user: current_user, size: "lg", classes: ["me-3"])
.flex-grow-1
= f.file_field :profile_picture, accept: APP_CONFIG[:accepted_image_formats].join(","), data: { cropper_target: "input", action: "cropper#change" }

Expand Down
2 changes: 1 addition & 1 deletion app/views/shared/_question.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- if type == "discover"
.flex-shrink-0
%a{ href: user_screen_name(q.user, link_only: true) }
%img.avatar-md.me-2{ src: q.user&.profile_picture&.url(:small), loading: :lazy }
= render AvatarComponent.new(user: q.user, size: "md", classes: ["me-2"])
.flex-grow-1
%h6.text-muted.answerbox__question-user
- if type.nil? && q.direct
Expand Down
2 changes: 1 addition & 1 deletion app/views/tabs/_feed.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
%p.px-4.pb-2
- list.members.each do |member|
%a{ href: user_path(member.user), title: member.user.screen_name, data: { bs_toggle: :tooltip, bs_placement: :top } }
%img.avatar-xs{ src: member.user.profile_picture.url(:small), loading: :lazy }
= render AvatarComponent.new(user: member.user, size: "xs")
- if !list && lists.empty?
.p-3= t(".lists.notice_html")
- lists.each do |list|
Expand Down
39 changes: 39 additions & 0 deletions spec/components/avatar_component_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe AvatarComponent, type: :component do
let(:user) { FactoryBot.create(:user) }

it "renders an avatar" do
expect(
render_inline(described_class.new(user:, size: "sm")).to_html,
).to include(
"no_avatar.png",
)
end

it "gets the medium version of a profile picture if requested" do
expect(
render_inline(described_class.new(user:, size: "md")).to_html,
).to include(
"medium/",
)
end

it "gets the large version of a profile picture if requested" do
expect(
render_inline(described_class.new(user:, size: "xl")).to_html,
).to include(
"large/",
)
end

it "includes additionally passed classes" do
expect(
render_inline(described_class.new(user:, size: "md", classes: %w[first-class second-class])).to_html,
).to include(
'class="avatar-md first-class second-class"',
)
end
end
4 changes: 4 additions & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
require "rspec/its"
require "devise"
require "rspec-sidekiq"
require "view_component/test_helpers"
require "view_component/system_test_helpers"

# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
Expand Down Expand Up @@ -68,6 +70,8 @@
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :helper
config.include Devise::Test::ControllerHelpers, type: :view
config.include ViewComponent::TestHelpers, type: :component
config.include ViewComponent::SystemTestHelpers, type: :component
end

Shoulda::Matchers.configure do |config|
Expand Down