diff --git a/app/assets/images/icons/fontawesome/arrow-up-right-from-square-solid.svg b/app/assets/images/icons/fontawesome/arrow-up-right-from-square-solid.svg new file mode 100644 index 000000000..d00a2680a --- /dev/null +++ b/app/assets/images/icons/fontawesome/arrow-up-right-from-square-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/fontawesome/avocado-solid.svg b/app/assets/images/icons/fontawesome/avocado-solid.svg new file mode 100644 index 000000000..93e055d43 --- /dev/null +++ b/app/assets/images/icons/fontawesome/avocado-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/fontawesome/browser-solid.svg b/app/assets/images/icons/fontawesome/browser-solid.svg new file mode 100644 index 000000000..5be26b791 --- /dev/null +++ b/app/assets/images/icons/fontawesome/browser-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/fontawesome/pen-solid.svg b/app/assets/images/icons/fontawesome/pen-solid.svg new file mode 100644 index 000000000..a334f2a65 --- /dev/null +++ b/app/assets/images/icons/fontawesome/pen-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/components/ui/button_component.rb b/app/components/ui/button_component.rb index 863ae6af8..977a62864 100644 --- a/app/components/ui/button_component.rb +++ b/app/components/ui/button_component.rb @@ -11,7 +11,8 @@ class Ui::ButtonComponent < ApplicationComponent warning: "btn-warning", error: "btn-error", ghost: "btn-ghost", - link: "btn-link" + link: "btn-link", + none: "" } SIZE_MAPPING = { diff --git a/app/controllers/speakers_controller.rb b/app/controllers/speakers_controller.rb index ab169dd32..19c1efef3 100644 --- a/app/controllers/speakers_controller.rb +++ b/app/controllers/speakers_controller.rb @@ -25,7 +25,10 @@ def index # GET /speakers/1 def show @talks = @speaker.talks.with_essential_card_data.order(date: :desc) + @topics = @speaker.topics.approved.tally.sort_by(&:last).reverse.map(&:first) + @back_path = speakers_path + set_meta_tags(@speaker) end diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index ac06526b8..7f604c22b 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -37,6 +37,9 @@ application.register("spotlight-search", SpotlightSearchController) import TalksNavigationController from "./talks_navigation_controller" application.register("talks-navigation", TalksNavigationController) +import ToggableController from "./toggable_controller" +application.register("toggable", ToggableController) + import TooltipController from "./tooltip_controller" application.register("tooltip", TooltipController) diff --git a/app/javascript/controllers/toggable_controller.js b/app/javascript/controllers/toggable_controller.js new file mode 100644 index 000000000..c6e732c3e --- /dev/null +++ b/app/javascript/controllers/toggable_controller.js @@ -0,0 +1,27 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + static targets = ['toggable', 'toggle'] + + static values = { + hideText: { + type: String, + default: 'hide' + } + } + + connect () { + this.toggleText = this.toggleTarget.textContent + } + + toggle () { + this.toggleTarget.textContent = this.nextToggleText + this.toggableTargets.forEach(toggable => toggable.classList.toggle('hidden')) + } + + get nextToggleText () { + return (this.toggleTarget.textContent === this.toggleText) + ? this.hideTextValue + : this.toggleText + } +} diff --git a/app/jobs/speaker/enhance_profile_job.rb b/app/jobs/speaker/enhance_profile_job.rb index 9d37cf182..4f7c2cdb4 100644 --- a/app/jobs/speaker/enhance_profile_job.rb +++ b/app/jobs/speaker/enhance_profile_job.rb @@ -33,7 +33,7 @@ def perform(speaker:, sleep: 0) website: speaker.website.presence || profile.blog || "" ) - speaker.broadcast_about + speaker.broadcast_header sleep(sleep) end diff --git a/app/models/speaker.rb b/app/models/speaker.rb index 9789eb552..f6194e538 100644 --- a/app/models/speaker.rb +++ b/app/models/speaker.rb @@ -53,6 +53,7 @@ class Speaker < ApplicationRecord has_many :talks, through: :speaker_talks, inverse_of: :speakers has_many :events, -> { distinct }, through: :talks, inverse_of: :speakers has_many :aliases, class_name: "Speaker", foreign_key: "canonical_id" + has_many :topics, through: :talks belongs_to :canonical, class_name: "Speaker", optional: true belongs_to :user, primary_key: :github_handle, foreign_key: :github, optional: true @@ -159,8 +160,8 @@ def fetch_bsky_metadata! Speaker::FetchBskyMetadata.new.perform(speaker: self) end - def broadcast_about - broadcast_update_to self, target: dom_id(self, :about), partial: "speakers/about", locals: {speaker: self} + def broadcast_header + broadcast_update target: dom_id(self, :header_content), partial: "speakers/header_content", locals: {speaker: self} end def valid_website_url diff --git a/app/views/events/_header.html.erb b/app/views/events/_header.html.erb index dbae3fe48..6bcd401ed 100644 --- a/app/views/events/_header.html.erb +++ b/app/views/events/_header.html.erb @@ -18,7 +18,7 @@
- <%= link_to "Visit Website", event.organisation.website, class: "btn btn-primary w-full", target: "_blank" %> + <%= link_to "Visit Website", event.website, class: "btn btn-neutral w-full", target: "_blank" %> <%= link_to "View all #{event.organisation.name} events", event.organisation, class: "btn w-full" %>
diff --git a/app/views/speakers/_actions.html.erb b/app/views/speakers/_actions.html.erb index 3f3ef2f53..5b0d5ae68 100644 --- a/app/views/speakers/_actions.html.erb +++ b/app/views/speakers/_actions.html.erb @@ -1,3 +1 @@ -
- <%= render "speakers/actions/#{user_kind}", speaker: speaker %> -
+<%= render "speakers/actions/#{user_kind}", speaker: speaker %> diff --git a/app/views/speakers/_header.html.erb b/app/views/speakers/_header.html.erb new file mode 100644 index 000000000..bd884c500 --- /dev/null +++ b/app/views/speakers/_header.html.erb @@ -0,0 +1,13 @@ +
+ <%= render "speakers/header_content", speaker: speaker %> + +
+ <% if speaker.website.present? %> + <%= link_to speaker.website, class: "btn btn-sm btn-neutral w-full tooltip tooltip-bottom flex place-content-center", target: "_blank", data: {tip: speaker.website} do %> + <%= fa "arrow-up-right-from-square", size: :xs, class: "fill-white" %> Visit Website + <% end %> + <% end %> + + <%= render "speakers/actions", speaker: speaker %> +
+
diff --git a/app/views/speakers/_header_content.html.erb b/app/views/speakers/_header_content.html.erb new file mode 100644 index 000000000..2d48a2041 --- /dev/null +++ b/app/views/speakers/_header_content.html.erb @@ -0,0 +1,36 @@ +
+
+ <%= image_tag speaker.github_avatar_url(size: 200), + class: "rounded-full border border-[#D9DFE3] size-24 md:size-36", + height: 200, + width: 200, + alt: "GitHub picture profile of #{speaker.github}", + loading: :lazy %> + + <% if speaker.verified? %> +
+ <%= fa("badge-check", class: "fill-white", size: :xs) %> + + Verified +
+ <% end %> +
+ +
+
+

<%= speaker.name %>

+ + <% if speaker.pronouns.present? && ["dont_specify", "not_specified"].exclude?(speaker.pronouns_type) %> + (<%= speaker.pronouns %>) + <% end %> +
+ +

+ <%= speaker.bio %> +

+ +
+ <%= render "speakers/socials", speaker: speaker %> +
+
+
diff --git a/app/views/speakers/_about.html.erb b/app/views/speakers/_socials.html.erb similarity index 50% rename from app/views/speakers/_about.html.erb rename to app/views/speakers/_socials.html.erb index 883093eb2..97bc8b31e 100644 --- a/app/views/speakers/_about.html.erb +++ b/app/views/speakers/_socials.html.erb @@ -1,51 +1,39 @@ -
-
+
+
<% if speaker.github.present? %> - <%= link_to "https://www.github.com/#{speaker.github}", target: "_blank", class: "hover:fill-gray-500" do %> + <%= link_to "https://www.github.com/#{speaker.github}", target: "_blank", class: "hover:bg-black hover:fill-white bg-white border rounded-full p-2 tooltip tooltip-top", data: {tip: speaker.github} do %> <%= fab("github") %> <% end %> <% end %> <% if speaker.twitter.present? %> - <%= link_to "https://www.x.com/#{speaker.twitter}", target: "_blank", class: "hover:fill-gray-500" do %> + <%= link_to "https://www.x.com/#{speaker.twitter}", target: "_blank", class: "hover:bg-[#74C0FC] hover:fill-white bg-white border rounded-full p-2 tooltip tooltip-top", data: {tip: speaker.twitter} do %> <%= fab("twitter") %> <% end %> <% end %> + <% if speaker.bsky.present? %> + <%= link_to "https://bsky.app/profile/#{speaker.bsky}", target: "_blank", class: "hover:bg-[#0085FF] hover:fill-white bg-white border rounded-full p-2 tooltip tooltip-top", data: {tip: speaker.bsky} do %> + <%= fab("bluesky") %> + <% end %> + <% end %> + <% if speaker.mastodon.present? %> - <%= link_to speaker.mastodon, target: "_blank", class: "hover:fill-gray-500" do %> + <%= link_to speaker.mastodon, target: "_blank", class: "hover:bg-[#6364FF] hover:fill-white bg-white border rounded-full p-2 tooltip tooltip-top", data: {tip: speaker.mastodon} do %> <%= fab("mastodon") %> <% end %> <% end %> <% if speaker.linkedin.present? %> - <%= link_to "https://www.linkedin.com/in/#{speaker.linkedin}", target: "_blank", class: "hover:fill-gray-500" do %> + <%= link_to "https://www.linkedin.com/in/#{speaker.linkedin}", target: "_blank", class: "hover:bg-[#0A66C2] hover:fill-white bg-white border rounded-full p-2 tooltip tooltip-top", data: {tip: speaker.linkedin} do %> <%= fab("linkedin") %> <% end %> <% end %> - <% if speaker.bsky.present? %> - <%= link_to "https://bsky.app/profile/#{speaker.bsky}", target: "_blank", class: "hover:fill-gray-500" do %> - <%= fab("bluesky") %> - <% end %> - <% end %> - <% if speaker.speakerdeck.present? %> - <%= link_to "https://speakerdeck.com/#{speaker.speakerdeck}", target: "_blank", class: "hover:fill-gray-500" do %> + <%= link_to "https://speakerdeck.com/#{speaker.speakerdeck}", target: "_blank", class: "hover:bg-[#009287] hover:fill-white bg-white border rounded-full p-2 tooltip tooltip-top", data: {tip: speaker.speakerdeck} do %> <%= fab("speaker-deck") %> <% end %> <% end %>
- - <% if speaker.website.present? %> - - Website: <%= link_to speaker.website, speaker.valid_website_url, target: "_blank" %> - - <% end %> - - <% if speaker.bio.present? %> - - Bio: <%= speaker.bio %> - - <% end %>
diff --git a/app/views/speakers/actions/_admin.html.erb b/app/views/speakers/actions/_admin.html.erb index d555c0bbb..996d29ed5 100644 --- a/app/views/speakers/actions/_admin.html.erb +++ b/app/views/speakers/actions/_admin.html.erb @@ -1,26 +1,28 @@ -
- <%= ui_button url: edit_speaker_path(@speaker), - kind: :secondary, - data: {turbo_frame: "modal"}, - outline: true, - size: :sm do %> - <%= heroicon :pencil_square %> - Update speaker page - <% end %> +<%= ui_button url: edit_speaker_path(@speaker), + kind: :none, + data: {turbo_frame: "modal"}, + size: :sm, + class: "w-full" do %> + <%= fa :pen, size: :xs %> + Update speaker page +<% end %> - <% if speaker.github.present? || speaker.bsky.present? %> - <%= ui_button url: speakers_enhance_path(@speaker), method: :put, kind: :ghost, size: :sm do %> - Enhance Profile - <% end %> +<% if speaker.github.present? || speaker.bsky.present? %> + <%= ui_button url: speakers_enhance_path(@speaker), method: :put, kind: :none, size: :sm, class: "w-full", form_class: "w-full" do %> + <%= fab :github, size: :xs %> + Fetch from GitHub <% end %> +<% end %> - <%= ui_button url: avo.resources_speaker_path(@speaker), target: :_blank, kind: :ghost, size: :sm do %> - Open in Avo - <% end %> +<%= ui_button url: avo.resources_speaker_path(@speaker), target: :_blank, kind: :none, size: :sm, class: "w-full" do %> + <%= fa :avocado, size: :xs %> + + Open in Avo +<% end %> - <% if Rails.env.development? %> - <%= ui_button url: speaker_url(@speaker, host: "https://rubyvideo.dev", port: 443), target: :_blank, kind: :ghost, size: :sm do %> - Show on RubyVideo.dev - <% end %> +<% if Rails.env.development? %> + <%= ui_button url: speaker_url(@speaker, host: "https://rubyvideo.dev", port: 443), target: :_blank, kind: :none, size: :sm, class: "w-full" do %> + <%= fa :browser, size: :xs %> + Show on RubyVideo.dev <% end %> -
+<% end %> diff --git a/app/views/speakers/actions/_anonymous.html.erb b/app/views/speakers/actions/_anonymous.html.erb index 2c00bf858..0f213c7a7 100644 --- a/app/views/speakers/actions/_anonymous.html.erb +++ b/app/views/speakers/actions/_anonymous.html.erb @@ -3,13 +3,13 @@ Help us improve the speaker profile by adding a GitHub handle <% end %> <% else %> - <% if !speaker.verified? %> - <%= ui_button url: edit_speaker_path(speaker), kind: :secondary, outline: true, size: :sm, data: {turbo_frame: "modal"} do %> - <%= heroicon :pencil_square %> - Suggest improvements - <% end %> + <%= ui_button url: edit_speaker_path(speaker), kind: :none, size: :sm, data: {turbo_frame: "modal"} do %> + <%= fa :pen, size: :xs %> + Suggest improvements + <% end %> -
+ <% if !speaker.verified? %> +
Are you <%= speaker.name %>? <%= link_to sign_in_path, class: "link link-neutral font-light", data: {turbo_frame: "modal"} do %> Claim your profile to manage and edit content. diff --git a/app/views/speakers/actions/_owner.html.erb b/app/views/speakers/actions/_owner.html.erb index 3d16a91a8..7d301f007 100644 --- a/app/views/speakers/actions/_owner.html.erb +++ b/app/views/speakers/actions/_owner.html.erb @@ -1,5 +1,4 @@ - -<%= ui_button url: edit_speaker_path(speaker), kind: :secondary, outline: true, size: :sm, data: {turbo_frame: "modal"} do %> - <%= heroicon :pencil_square %> +<%= ui_button url: edit_speaker_path(speaker), kind: :none, size: :sm, data: {turbo_frame: "modal"} do %> + <%= fa :pen, size: :xs %> Update my profile <% end %> diff --git a/app/views/speakers/actions/_signed_in.html.erb b/app/views/speakers/actions/_signed_in.html.erb index 9a2df2d33..65a39a0b7 100644 --- a/app/views/speakers/actions/_signed_in.html.erb +++ b/app/views/speakers/actions/_signed_in.html.erb @@ -3,8 +3,8 @@ Help us improve the speaker profile by adding a GitHub handle <% end %> <% else %> - <%= ui_button url: edit_speaker_path(speaker), kind: :secondary, outline: true, size: :sm, data: {turbo_frame: "modal"} do %> - <%= heroicon :pencil_square %> + <%= ui_button url: edit_speaker_path(speaker), kind: :none, size: :sm, data: {turbo_frame: "modal"} do %> + <%= fa :pen, size: :xs %> Suggest improvements <% end %> <% end %> diff --git a/app/views/speakers/show.html.erb b/app/views/speakers/show.html.erb index 25efac5d0..e62b0e6c4 100644 --- a/app/views/speakers/show.html.erb +++ b/app/views/speakers/show.html.erb @@ -1,47 +1,35 @@ <%= turbo_refreshes_with method: :morph, scroll: :preserve %> +<%= turbo_stream_from @speaker %>
-
-
-
-

<%= @speaker.name %>

- <% if @speaker.pronouns.present? && ["dont_specify", "not_specified"].exclude?(@speaker.pronouns_type) %> - (<%= @speaker.pronouns %>) - <% end %> -
+ <%= render partial: "speakers/header", locals: {speaker: @speaker} %> -
- <%= image_tag @speaker.avatar_url(size: 200), - class: "rounded-full mt-4", - height: 200, - width: 200, - alt: "GitHub picture profile of #{@speaker.github}", - loading: :lazy %> + <% if @topics.any? %> +
+ <%= render partial: "topics/badge_list", locals: {topics: @topics, back_to_url: request.fullpath, back_to_title: @speaker.name} %> +
+ <% end %> - <% if @speaker.verified? %> -
Verified
- <% end %> -
+
- <%= render "speakers/about", speaker: @speaker %> - <%= render "speakers/actions", speaker: @speaker %> -
+
+ <% if @speaker.talks.any? %> + -
-
- <%= render partial: "talks/card", - collection: @talks, - as: :talk, - locals: { - favoritable: true, - user_favorite_talks_ids: @user_favorite_talks_ids, - watched_talks_ids: user_watched_talks_ids, - back_to: request.fullpath, - back_to_title: @speaker.name - } %> +
+
+ <%= render partial: "talks/card", + collection: @talks, + as: :talk, + locals: { + favoritable: true, + user_favorite_talks_ids: @user_favorite_talks_ids, + watched_talks_ids: user_watched_talks_ids, + back_to: request.fullpath, + back_to_title: @speaker.name + } %> +
-
+ <% end %>
- - <%= turbo_stream_from @speaker %>
diff --git a/app/views/talks/_talk.html.erb b/app/views/talks/_talk.html.erb index e7feee0e4..c5ba598cf 100644 --- a/app/views/talks/_talk.html.erb +++ b/app/views/talks/_talk.html.erb @@ -137,13 +137,7 @@
<% if talk.approved_topics.any? %> -
- <% talk.approved_topics.each do |topic| %> - <%= link_to topic_path(topic, back_to: request.fullpath, back_to_title: talk.title) do %> -
#<%= topic.name.parameterize %>
- <% end %> - <% end %> -
+ <%= render partial: "topics/badge_list", locals: {topics: talk.approved_topics, back_to_url: request.fullpath, back_to_title: talk.title} %> <% end %>
diff --git a/app/views/topics/_badge.html.erb b/app/views/topics/_badge.html.erb new file mode 100644 index 000000000..44ee8c430 --- /dev/null +++ b/app/views/topics/_badge.html.erb @@ -0,0 +1,3 @@ +<%= link_to topic_path(topic, back_to: back_to_url, back_to_title: back_to_title) do %> +
#<%= topic.name.parameterize %>
+<% end %> diff --git a/app/views/topics/_badge_list.html.erb b/app/views/topics/_badge_list.html.erb new file mode 100644 index 000000000..060ca436c --- /dev/null +++ b/app/views/topics/_badge_list.html.erb @@ -0,0 +1,16 @@ +<% limit ||= 8 %> +<% more_topics = topics.to_a.from(limit) %> + +
+ <%= render partial: "topics/badge", collection: topics.first(limit), as: :topic, locals: {back_to_url: back_to_url, back_to_title: back_to_title} %> + + <% more_topics.each do |topic| %> + + <% end %> + + <% if more_topics.any? %> + <%= more_topics.count %> more + <% end %> +
diff --git a/test/system/speakers_test.rb b/test/system/speakers_test.rb index 715aacd94..7de85ff92 100644 --- a/test/system/speakers_test.rb +++ b/test/system/speakers_test.rb @@ -27,7 +27,7 @@ class SpeakersTest < ApplicationSystemTestCase wait_for_turbo_stream_connected(streamable: @speaker) @speaker.update(bio: "New bio") - @speaker.broadcast_about + @speaker.broadcast_header assert_text "New bio" end