Skip to content

Commit

Permalink
Add conference speakers page (#324)
Browse files Browse the repository at this point in the history
* Add conference speakers page

* Use Turbo Frame to navigate between subpages

* Tweak Frame Navigation, add kind titles, group by kind on speaker page, remove hero banner flicker

* Update Q&A and AMA kind matching

* Fix `Heroes` spelling

* Make sure speakers are uniq per kind

* Add interview kind to speakers index
  • Loading branch information
marcoroth authored Nov 27, 2024
1 parent 00b38da commit db95e3f
Show file tree
Hide file tree
Showing 14 changed files with 166 additions and 96 deletions.
15 changes: 15 additions & 0 deletions app/controllers/events/speakers_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class Events::SpeakersController < ApplicationController
skip_before_action :authenticate_user!, only: %i[index]
before_action :set_event, only: %i[index]

def index
end

private

def set_event
@event = Event.includes(:organisation, talks: {speakers: :user}).find_by!(slug: params[:event_slug])

redirect_to schedule_event_path(@event.canonical), status: :moved_permanently if @event.canonical.present?
end
end
11 changes: 11 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ def back_path
@back_path || root_path
end

def active_link_to(text = nil, path = nil, active_class: "", **options, &)
path ||= text

classes = active_class.presence || "active"
options[:class] = class_names(options[:class], classes) if current_page?(path)

return link_to(path, options, &) if block_given?

link_to text, path, options
end

def footer_credits
output = ["Made with"]
output << heroicon(:heart, variant: :solid, size: :sm, class: "text-primary inline")
Expand Down
2 changes: 1 addition & 1 deletion app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def description
keynotes = keynote_speakers.any? ? %(, including keynotes by #{keynote_speakers.map(&:name).to_sentence}) : ""

<<~DESCRIPTION
#{organisation.name} is a #{organisation.frequency} #{organisation.kind}#{held_in_sentence} and features #{talks.count} #{"talk".pluralize(talks.count)} from various speakers#{keynotes}.
#{organisation.name} is a #{organisation.frequency} #{organisation.kind}#{held_in_sentence} and features #{talks.size} #{"talk".pluralize(talks.size)} from various speakers#{keynotes}.
DESCRIPTION
end

Expand Down
24 changes: 13 additions & 11 deletions app/models/talk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Talk < ApplicationRecord
# enums
enum :video_provider, %w[youtube mp4 scheduled not_published not_recorded].index_by(&:itself)
enum :kind,
%w[talk keynote lightning_talk panel workshop gameshow podcast q_and_a discussion fireside_chat
%w[keynote talk lightning_talk panel workshop gameshow podcast q_and_a discussion fireside_chat
interview award].index_by(&:itself)

# attributes
Expand Down Expand Up @@ -378,28 +378,30 @@ def set_kind
end

self.kind = case title
when /.*(keynote:|opening\ keynote|closing\ keynote|keynote|opening\ keynote|closing\ keynote).*/i
when /^(keynote:|keynote|opening\ keynote:|opening\ keynote|closing\ keynote:|closing\ keynote).*/i
:keynote
when /.*(lightning\ talk:|lightning\ talk|lightning\ talks|micro\ talk:|micro\ talk).*/i
when /^(lightning\ talk:|lightning\ talk|lightning\ talks|micro\ talk:|micro\ talk).*/i
:lightning_talk
when /.*(panel:|panel).*/i
:panel
when /.*(workshop:|workshop).*/i
when /^(workshop:|workshop).*/i
:workshop
when /.*(gameshow|game\ show|gameshow:|game\ show:).*/i
when /^(gameshow|game\ show|gameshow:|game\ show:).*/i
:gameshow
when /.*(podcast:|podcast\ recording:|live\ podcast:).*/i
when /^(podcast:|podcast\ recording:|live\ podcast:).*/i
:podcast
when /.*(q&a|q&a:|ama|q&a\ with|ruby\ committers\ vs\ the\ world|ruby\ committers\ and\ the\ world).*/i
when /.*(q&a|q&a:|q&a\ with|ruby\ committers\ vs\ the\ world|ruby\ committers\ and\ the\ world).*/i,
/.*(AMA)$/,
/^(AMA:)/
:q_and_a
when /.*(fishbowl:|fishbowl\ discussion:|discussion:|discussion).*/i
when /^(fishbowl:|fishbowl\ discussion:|discussion:|discussion).*/i
:discussion
when /.*(fireside\ chat:|fireside\ chat).*/i
when /^(fireside\ chat:|fireside\ chat).*/i
:fireside_chat
when /^(award:|award\ show|ruby\ heroes\ awards|ruby\ heroes\ award|rails\ luminary).*/i
:award
when /^(interview:|interview\ with).*/i
:interview
when /.*(award:|award\ show|ruby\ hero\ awards|ruby\ hero\ award|rails\ luminary).*/i
:award
else
:talk
end
Expand Down
29 changes: 29 additions & 0 deletions app/views/events/_header.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<div class="w-full md:h-[198px] lg:h-[267px] xl:h-[336px] 2xl:h-[405px] justify-center align-center flex mt-3 border-t border-b" style="view-transition-name: hero; background: <%= event.banner_background %>">
<%= image_tag image_path(@event.banner_image_path), class: "w-full md:container" %>
</div>

<div class="container py-8">
<div class="block lg:flex gap-8 align-center justify-between">
<div class="flex flex-col lg:flex-row gap-8 items-center lg:justify-right text-center lg:text-left mb-6 lg:mb-0">
<%= image_tag image_path(event.avatar_image_path),
class: "rounded-full border border-[#D9DFE3] size-24 md:size-36",
alt: "#{event.name} Avatar",
style: "view-transition-name: avatar",
loading: :lazy %>

<div class="flex-col flex justify-center">
<h1 class="mb-2 text-black font-bold" style="view-transition-name: title"><%= event.name %></h1>
<h3 class="text-[#636B74]"><%= event.location %><%= event.formatted_dates %></h3>
</div>
</div>

<div class="flex flex-col gap-3 place-items-center">
<%= link_to "Visit Website", event.organisation.website, class: "btn btn-primary w-full", target: "_blank" %>
<%= link_to "View all #{event.organisation.name} events", event.organisation, class: "btn w-full" %>
</div>
</div>

<p class="mt-6 text-[#636B74] max-w-[700px]">
<%= event.description %>
</p>
</div>
11 changes: 11 additions & 0 deletions app/views/events/_navigation.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div role="tablist" class="mb-12 tabs tabs-bordered">
<%= active_link_to "Talks", event_path(event), class: "tab", active_class: "tab-active", role: "tab" %>
<%= active_link_to "Speakers", event_speakers_path(event), class: "tab", active_class: "tab-active", role: "tab" %>
<% if false %>
<a role="tab" class="tab">Schedule</a>
<a role="tab" class="tab">Organizers</a>
<a role="tab" class="tab">Photos</a>
<a role="tab" class="tab">Sponsors</a>
<% end %>
</div>
109 changes: 35 additions & 74 deletions app/views/events/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,79 +1,40 @@
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>

<div class="w-full max-h-92 justify-center align-center flex mt-3 border-t border-b" style="view-transition-name: hero; background: <%= @event.banner_background %>">
<%= image_tag image_path(@event.banner_image_path), class: "w-full max-h- aspect-auto container" %>
</div>

<div class="container py-8">
<div class="block lg:flex gap-8 align-center justify-between">
<div class="flex flex-col lg:flex-row gap-8 items-center lg:justify-right text-center lg:text-left mb-6 lg:mb-0">
<%= image_tag image_path(@event.avatar_image_path),
class: "rounded-full border border-[#D9DFE3] size-24 md:size-36",
alt: "#{@event.name} Avatar",
style: "view-transition-name: avatar",
loading: :lazy %>

<div class="flex-col flex justify-center">
<h1 class="mb-2 text-black font-bold"><%= @event.name %></h1>
<h3 class="text-[#636B74]"><%= @event.location %><%= @event.formatted_dates %></h3>
</div>
</div>

<div class="flex flex-col gap-3 place-items-center">
<%= link_to "Visit Website", @event.website, class: "btn btn-primary w-full", target: "_blank" %>
<%= link_to "View all #{@event.organisation.name} events", @event.organisation, class: "btn w-full" %>
</div>
</div>

<p class="mt-6 text-[#636B74] max-w-[700px]">
<%= @event.description %>
</p>

<% if false %>
<div role="tablist" class="mt-12 tabs tabs-bordered">
<a role="tab" class="tab tab-active">Talks</a>
<a role="tab" class="tab">Schedule</a>
<a role="tab" class="tab">Speakers</a>
<a role="tab" class="tab">Organizers</a>
<a role="tab" class="tab">Photos</a>
<a role="tab" class="tab">Sponsors</a>
</div>
<% end %>
</div>

<div class="container py-8">

<div class="flex items-start flex-wrap gap-8 sm:flex-nowrap w-full">
<div class="w-full">
<div id="talks" class="min-w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 h-full">
<%= render partial: "talks/card",
collection: @talks,
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: @event.name
},
as: :talk %>
</div>
<%= render partial: "header", locals: {event: @event} %>
<%= turbo_frame_tag dom_id(@event), data: {turbo_action: "advance", turbo_frame: "_top"} do %>
<div class="container py-8">
<%= render partial: "navigation", locals: {event: @event} %>

<div class="flex items-start flex-wrap gap-8 sm:flex-nowrap w-full">
<div class="w-full">
<div id="talks" class="min-w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 h-full">
<%= render partial: "talks/card",
collection: @talks,
locals: {
favoritable: true,
user_favorite_talks_ids: @user_favorite_talks_ids,
back_to: request.fullpath,
back_to_title: @event.name
},
as: :talk %>
</div>

<% if @talks.empty? %>
<div class="flex justify-center border rounded" style="padding-top: 3rem; padding-bottom: 3rem; padding-right: 1rem; padding-left: 1rem">
<div class="text-center text-gray-600">
<p>
<b>No talks found for this event.</b>
</p>
<p class="mt-2">
Please
<a href="https://github.com/adrienpoly/rubyvideo/issues/new?title=<%= CGI.escape("#{@event.name} Recordings") %>&body=<%= CGI.escape("<!-- Your Content Here -->\n\nIssue opened from event page: #{event_url(@event)}") %>" class="text-primary" target="_blank">open an issue on GitHub</a>
if you know how to get access to the recordings of this event. Thank you!
</p>
<% if @talks.empty? %>
<div class="flex justify-center border rounded" style="padding-top: 3rem; padding-bottom: 3rem; padding-right: 1rem; padding-left: 1rem">
<div class="text-center text-gray-600">
<p>
<b>No talks found for this event.</b>
</p>
<p class="mt-2">
Please
<a href="https://github.com/adrienpoly/rubyvideo/issues/new?title=<%= CGI.escape("#{@event.name} Recordings") %>&body=<%= CGI.escape("<!-- Your Content Here -->\n\nIssue opened from event page: #{event_url(@event)}") %>" class="text-primary" target="_blank">open an issue on GitHub</a>
if you know how to get access to the recordings of this event. Thank you!
</p>
</div>
</div>
</div>
<% end %>
<% end %>
<%== pagy_nav(@pagy) if @pagy.pages > 1 %>
<%== pagy_nav(@pagy) if @pagy.pages > 1 %>
</div>
</div>
</div>
</div>
<% end %>
40 changes: 40 additions & 0 deletions app/views/events/speakers/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<%= render partial: "events/header", locals: {event: @event} %>
<%= turbo_frame_tag dom_id(@event), data: {turbo_action: "advance", turbo_frame: "_top"} do %>
<div class="container py-8">
<%= render partial: "events/navigation", locals: {event: @event} %>

<div class="flex items-start flex-wrap gap-8 sm:flex-nowrap w-full">
<div class="w-full">
<% titles = {
keynote: "Keynote Speaker",
talk: "Speaker",
lightning_talk: "Lightning Talk Speaker",
panel: "Panelist",
discussion: "Panelist",
gameshow: "Game Show Host",
workshop: "Workshop Instructor",
podcast: "Podcast Host/Participant",
q_and_a: "Q&A Host/Participant",
fireside_chat: "Fireside Chat Host/Participant",
interview: "Interviewer/Interviewee",
award: "Award Presenter/Winner"
} %>
<% grouped = @event.talks.flat_map { |talk| talk.speakers.map { |speaker| [talk.kind, speaker] } }.group_by(&:first).transform_values(&:uniq).sort_by { |kind, _speakers| Talk.kinds.values.index(kind) } %>
<% grouped.each do |kind, speakers| %>
<h2 class="mb-6"><%= titles[kind.to_sym].pluralize(speakers.count) %></h2>

<div id="keynote-speakers" class="mb-12 min-w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 h-full">
<% speakers.each do |kind, speaker| %>
<div class="border rounded-lg bg-white hover:bg-gray-200 transition-bg duration-300 ease-in-out">
<%= render partial: "talks/speaker", locals: {speaker: speaker} %>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
</div>
<% end %>
2 changes: 1 addition & 1 deletion app/views/talks/_card.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<% language = Language.by_code(talk.language) %>

<div class="card card-compact bg-white border w-full" id="<%= dom_id talk %>">
<%= link_to talk_path(talk, back_to: back_to, back_to_title: back_to_title), class: "flex aspect-video overflow-hidden relative group", data: {action: "mouseover->preserve-scroll#updateLinkBackToWithScrollPosition"} do %>
<%= link_to talk_path(talk, back_to: back_to, back_to_title: back_to_title), class: "flex aspect-video overflow-hidden relative group", data: {action: "mouseover->preserve-scroll#updateLinkBackToWithScrollPosition", turbo_frame: "_top"} do %>
<% if language && language != "English" %>
<div class="absolute top-0 left-0 z-20 m-3 p-1 px-2 bg-black/15 backdrop-blur-md rounded-full">
<span><%= language_to_emoji(language) %></span>
Expand Down
2 changes: 1 addition & 1 deletion app/views/talks/_speaker.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%= link_to speaker, class: "flex flex-col gap-4 hover:bg-gray-200 transition-bg duration-300 ease-in-out p-2 px-4 rounded-lg" do %>
<%= link_to speaker, class: "flex flex-col gap-4 hover:bg-gray-200 transition-bg duration-300 ease-in-out p-2 px-4 rounded-lg", data: {turbo_frame: "_top"} do %>
<div class="flex items-center gap-4">
<div class="avatar placeholder">
<div class="w-12 rounded-full bg-primary text-neutral-content">
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
resources :speakers, param: :slug, only: [:index, :show, :update, :edit]
resources :events, param: :slug, only: [:index, :show, :update, :edit] do
scope module: :events do
resources :speakers, only: [:index]
resources :talks, only: [:index]
end
end
Expand Down
4 changes: 2 additions & 2 deletions data/railsconf/railsconf-2014/videos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -713,8 +713,8 @@
video_provider: youtube
video_id: 6tQTwmIgclE

- title: Ruby Heros Awards 2014
raw_title: RailsConf 2014 - Ruby Heros
- title: Ruby Heroes Awards 2014
raw_title: RailsConf 2014 - Ruby Heroes
speakers:
- Gregg Pollack
event_name: RailsConf 2014
Expand Down
2 changes: 1 addition & 1 deletion load_testing/talks_payload.csv
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ railsconf-2014-refactoring-towards-component-based-rails-architectures
railsconf-2014-rails-as-an-soa-client-by-pete-hodgson
railsconf-2014-service-oriented-authenication-by-jeremy-green
railsconf-2014-authorization-in-a-service-oriented-environment-by-alan-cohen
railsconf-2014-ruby-heros
railsconf-2014-ruby-heroes
railsconf-2014-designing-the-apis-for-an-internal-set-of-services-by-alberto-leal
railsconf-2014-humor-in-the-code-by-baratunde-thurston
railsconf-2014-build-the-api-first-by-rosie-hoyem-and-sonja-hall
Expand Down
10 changes: 5 additions & 5 deletions test/models/talk_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@ class TalkTest < ActiveSupport::TestCase

test "should guess kind from title" do
kind_with_titles = {
talk: ["I love Ruby"],
keynote: ["Keynote: Something ", "foo Opening keynote bar", "closing keynote foo bar", "Keynote", "Keynote by Someone", "Opening Keynote", "Closing Keynote"],
talk: ["I love Ruby", "Beyond Code: Crafting effective discussions to further technical decision-making", "From LALR to IELR: A Lrama's Next Step"],
keynote: ["Keynote: Something ", "Opening keynote Something", "closing keynote Something", "Keynote", "Keynote by Someone", "Opening Keynote", "Closing Keynote"],
lightning_talk: ["Lightning Talk: Something", "lightning talk: Something", "Lightning talk: Something", "lightning talk", "Lightning Talks", "Lightning talks", "lightning talks", "Lightning Talks Day 1", "Lightning Talks (Day 1)", "Lightning Talks - Day 1", "Micro Talk: Something", "micro talk: Something", "micro talk: Something", "micro talk"],
panel: ["Panel: foo", "Panel", "Something Panel"],
workshop: ["Workshop: Something", "workshop: Something"],
gameshow: ["Gameshow", "Game Show", "Gameshow: Something", "Game Show: Something"],
podcast: ["Podcast: Something", "Podcast Recording: Something", "Live Podcast: Something"],
q_and_a: ["Q&A", "Q&A: Something", "Something AMA", "Q&A with Somebody", "Ruby Committers vs The World", "Ruby Committers and the World"],
q_and_a: ["Q&A", "Q&A: Something", "Something AMA", "Q&A with Somebody", "Ruby Committers vs The World", "Ruby Committers and the World", "AMA: Rails Core"],
discussion: ["Discussion: Something", "Discussion", "Fishbowl: Topic", "Fishbowl Discussion: Topic"],
fireside_chat: ["Fireside Chat: Something", "Fireside Chat"],
interview: ["Interview with Matz", "Interview: Something"],
award: ["Award: Something", "Award Show", "Ruby Hero Awards", "Ruby Hero Award", "Rails Luminary"]
award: ["Award: Something", "Award Show", "Ruby Heroes Awards", "Ruby Heroes Award", "Rails Luminary"]
}

kind_with_titles.each do |kind, titles|
titles.each do |title|
talk = Talk.new(title:)
talk.save!

assert_equal kind.to_s, talk.kind
assert_equal [kind.to_s, title], [talk.kind, talk.title]

talk.destroy!
end
Expand Down

0 comments on commit db95e3f

Please sign in to comment.