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

AO3-6880 Use pagy gem for pagination on select pages #5054

Merged
merged 16 commits into from
Feb 12, 2025
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ gem 'unicode_utils', '>=1.4.0'
gem "lograge" # https://github.com/roidrage/lograge

gem 'will_paginate', '>=3.0.2'
gem "pagy", "~> 9.3"
gem 'acts_as_list', '~> 0.9.7'
gem 'akismetor'

Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ GEM
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
orm_adapter (0.5.0)
pagy (9.3.3)
parallel (1.25.1)
parser (3.3.0.5)
ast (~> 2.4.1)
Expand Down Expand Up @@ -701,6 +702,7 @@ DEPENDENCIES
mysql2
n_plus_one_control
nokogiri (>= 1.8.5)
pagy (~> 9.3)
permit_yo
phraseapp-in-context-editor-ruby (>= 1.0.6)
pickle
Expand Down
24 changes: 24 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,30 @@ def sanitize_ac_params
end
end

include Pagy::Backend
def pagy(collection, **vars)
pagy_overflow_handler do
super
end
end

def pagy_query_result(query_result, **vars)
pagy_overflow_handler do
Pagy.new(
count: query_result.total_entries,
page: query_result.current_page,
limit: query_result.per_page,
**vars
)
end
end

def pagy_overflow_handler(*)
yield
rescue Pagy::OverflowError
nil
end
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the same behavior as now by not showing the pagination in case of overflow ("page 66 of 10").

pagy's alternative options for reference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not setting Pagy::DEFAULT[:overflow], the default is :empty_page instead of :exception, do we need to wrap calls in pagy_overflow_handler?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without the require 'pagy/extras/overflow' line, Pagy always raises an exception in case of overflow (source)


def display_auth_error
respond_to do |format|
format.html do
Expand Down
7 changes: 7 additions & 0 deletions app/controllers/bookmarks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ def index
end
end
set_own_bookmarks

@pagy =
if @bookmarks.respond_to?(:total_pages)
pagy_query_result(@bookmarks)
elsif @bookmarkable_items.respond_to?(:total_pages)
pagy_query_result(@bookmarkable_items)
end
end

# GET /:locale/bookmark/:id
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/readings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def index
@readings = @readings.where(toread: true)
@page_subtitle = ts("Marked For Later")
end
@readings = @readings.order("last_viewed DESC").page(params[:page])
@readings = @readings.order("last_viewed DESC")
@pagy, @readings = pagy(@readings)
end

def destroy
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/works_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def index
@works = Work.latest.for_blurb.to_a
end
set_own_works

@pagy = pagy_query_result(@works) if @works.respond_to?(:total_pages)
end

def collected
Expand Down
12 changes: 0 additions & 12 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -516,18 +516,6 @@ def first_paragraph(full_text, placeholder_text = 'No preview available.')
end
end

# change the default link renderer for will_paginate
def will_paginate(collection_or_options = nil, options = {})
if collection_or_options.is_a? Hash
options = collection_or_options
collection_or_options = nil
end
unless options[:renderer]
options = options.merge renderer: PaginationListLinkRenderer
end
super(*[collection_or_options, options].compact)
end

# spans for nesting a checkbox or radio button inside its label to make custom
# checkbox or radio designs
def label_indicator_and_text(text)
Expand Down
66 changes: 66 additions & 0 deletions app/helpers/pagination_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module PaginationHelper
include Pagy::Frontend

# change the default link renderer for will_paginate
def will_paginate(collection_or_options = nil, options = {})
if collection_or_options.is_a? Hash
options = collection_or_options
collection_or_options = nil
end
options = options.merge renderer: PaginationListLinkRenderer unless options[:renderer]
super(*[collection_or_options, options].compact)
end

# Cf https://github.com/ddnexus/pagy/blob/master/gem/lib/pagy/frontend.rb
# i18n-tasks-use t("pagy.prev")
# i18n-tasks-use t("pagy.next")
# i18n-tasks-use t("pagy.aria_label.nav")
def pagy_nav(pagy, id: nil, aria_label: nil, **vars)
return nil unless pagy

# Keep will_paginate behavior of showing nothing if only one page
return nil if pagy.series.length <= 1

id = %( id="#{id}") if id
a = pagy_anchor(pagy, **vars)

html = %(<h4 class="landmark heading">#{t('a11y.navigation')}</h4>)

html << %(<ol#{id} class="pagination actions pagy" role="navigation" #{nav_aria_label(pagy, aria_label: aria_label)}>)

prev_text = pagy_t("pagy.prev")
prev_a =
if (p_prev = pagy.prev)
a.call(p_prev, prev_text)
else
%(<span class="disabled">#{prev_text}</span>)
end
html << %(<li class="previous">#{prev_a}</li>)

pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
html << %(<li>)
html << case item
when Integer
a.call(item)
when String
%(<a role="link" aria-disabled="true" aria-current="page" class="current">#{pagy.label_for(item)}</a>)
when :gap
%(<span class="gap">#{pagy_t('pagy.gap')}</span>)
else
raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
end
html << %(</li>)
end

next_text = pagy_t("pagy.next")
next_a =
if (p_next = pagy.next)
a.call(p_next, next_text)
else
%(<span class="disabled">#{next_text}</span>)
end
html << %(<li class="next">#{next_a}</li>)

html << %(</ol>)
end
end
12 changes: 2 additions & 10 deletions app/views/bookmarks/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@
<p><%= ts("These are some of the latest bookmarks created on the Archive. To find more bookmarks, #{link_to 'choose a fandom', media_path} or #{link_to 'try our advanced search', search_bookmarks_path}.").html_safe %>
<% end %>

<% if @bookmarks.respond_to?(:total_pages) %>
<%= will_paginate @bookmarks %>
<% elsif @bookmarkable_items.respond_to?(:total_pages) %>
<%= will_paginate @bookmarkable_items %>
<% end %>
<%== pagy_nav @pagy %>
Fixed Show fixed Hide fixed

<!--main content-->
<h3 class="landmark heading"><%= ts("List of Bookmarks") %></h3>
Expand All @@ -71,9 +67,5 @@
<div id="dynamic-bookmark" class="dynamic hidden"></div>

<!--subnav-->
<% if @bookmarks.respond_to?(:total_pages) %>
<%= will_paginate @bookmarks %>
<% elsif @bookmarkable_items.respond_to?(:total_pages) %>
<%= will_paginate @bookmarkable_items %>
<% end %>
<%== pagy_nav @pagy %>
Fixed Show fixed Hide fixed
<!-- /subnav-->
2 changes: 1 addition & 1 deletion app/views/readings/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@
<!--/content-->

<!--subnav-->
<%= will_paginate @readings %>
<%== pagy_nav @pagy %>
<!--/subnav-->
8 changes: 2 additions & 6 deletions app/views/works/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@
<p><%= ts("These are some of the latest works posted to the Archive. To find more works, #{link_to 'choose a fandom', media_path} or #{link_to 'try our advanced search', search_works_path}.").html_safe %>
<% end %>

<% if @works.respond_to?(:total_pages) %>
<%= will_paginate @works %>
<% end %>
<%== pagy_nav @pagy %>
Fixed Show fixed Hide fixed

<!--main content-->
<h3 class="landmark heading"><%= ts("Listing Works") %></h3>
Expand All @@ -64,6 +62,4 @@
<% end %>
<!---/subnav-->

<% if @works.respond_to?(:total_pages) %>
<%= will_paginate @works %>
<% end %>
<%== pagy_nav @pagy %>
Fixed Show fixed Hide fixed
1 change: 1 addition & 0 deletions config/brakeman.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
:safe_methods:
- :pagy_nav
- :sanitize_field
10 changes: 10 additions & 0 deletions config/initializers/gem-plugin_config/pagy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

# https://ddnexus.github.io/pagy/docs/extras/i18n/
require "pagy/extras/i18n"

# See https://ddnexus.github.io/pagy/docs/api/pagy#variables
Pagy::DEFAULT[:limit] = ArchiveConfig.ITEMS_PER_PAGE
Pagy::DEFAULT[:size] = 9

Pagy::DEFAULT.freeze
5 changes: 5 additions & 0 deletions config/locales/views/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1565,6 +1565,11 @@ en:
orphan_user:
orphaning_bylines_only_message_html: Orphaning will automatically remove your personal information from <strong>bylines only</strong>. It will <strong>not</strong> automatically remove your personal information from anywhere else in the work(s). Social media accounts, contact information, email addresses, names, and any other personal information posted in the title, summary, notes, tags, work body, or comment text will <strong>not</strong> be automatically removed. Comments posted by other users will also <strong>not</strong> be affected by Orphaning. If you want information removed from these places, you should edit or delete it prior to Orphaning. <strong>Once you Orphan the work(s), you will no longer be able to delete information in these places yourself</strong>.
orphaning_works_message_html: 'Orphaning will <strong>permanently</strong> remove your username and/or pseud from the bylines of: the following work(s), their chapters, associated series, and any comments you may have left on them while logged into this account.'
pagy:
aria_label:
nav: Pagination
next: Next →
prev: "← Previous"
preferences:
index:
allow_collection_invitation: Allow others to invite my works to collections.
Expand Down
Loading