Skip to content

Commit 4d04f29

Browse files
committed
Extract work package activity tab pagination
1 parent e777bb6 commit 4d04f29

4 files changed

Lines changed: 383 additions & 79 deletions

File tree

app/controllers/work_packages/activities_tab_controller.rb

Lines changed: 1 addition & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
# ++
3030

3131
class WorkPackages::ActivitiesTabController < ApplicationController
32-
include Pagy::Backend
3332
include OpTurbo::ComponentStream
3433
include FlashMessagesOutputSafetyHelper
3534
include WorkPackages::ActivitiesTab::JournalSortingInquirable
@@ -192,83 +191,7 @@ def toggle_reaction # rubocop:disable Metrics/AbcSize
192191
private
193192

194193
def initialize_pagination
195-
anchor_type, target_journal_id = extract_target_journal_id
196-
197-
@paginator, @paginated_journals =
198-
if anchor_type && target_journal_id
199-
pagy_array_for_target_journal(anchor_type, target_journal_id)
200-
else
201-
pagy_array(base_journals)
202-
end
203-
204-
# For UI display: if user wants "oldest first" UI, reverse the array
205-
@paginated_journals = @paginated_journals.reverse if journal_sorting.asc?
206-
end
207-
208-
def extract_target_journal_id
209-
anchor = params[:anchor] # e.g., "comment-78758" (without #)
210-
return nil unless anchor
211-
212-
match = anchor.match(/^(comment|activity)-(\d+)$/)
213-
match && match.length == 3 ? [match[1].inquiry, match[2].to_i] : []
214-
end
215-
216-
def pagy_array_for_target_journal(anchor_type, target_journal_id)
217-
journals = base_journals
218-
219-
target_index = journals.find_index do |record|
220-
if anchor_type.comment?
221-
record.id == target_journal_id
222-
elsif anchor_type.activity?
223-
record.sequence_version == target_journal_id
224-
else
225-
false
226-
end
227-
end
228-
229-
if target_index
230-
target_page = (target_index / Pagy::DEFAULT[:limit]) + 1
231-
pagy_array(journals, page: target_page)
232-
else
233-
# Journal might be filtered out or deleted - fallback to page 1
234-
pagy_array(journals, page: 1)
235-
end
236-
end
237-
238-
def base_journals
239-
combine_and_sort_records(fetch_journals, fetch_revisions)
240-
end
241-
242-
def fetch_journals
243-
API::V3::Activities::ActivityEagerLoadingWrapper.wrap(fetch_ar_journals)
244-
end
245-
246-
def fetch_ar_journals
247-
@work_package
248-
.journals
249-
.internal_visible
250-
.includes(:user, :customizable_journals, :attachable_journals, :storable_journals, :notifications)
251-
.reorder(version: :desc) # Always fetch newest first for pagination
252-
.with_sequence_version
253-
end
254-
255-
def fetch_revisions
256-
@work_package.changesets.includes(:user, :repository)
257-
end
258-
259-
def combine_and_sort_records(journals, revisions)
260-
(journals + revisions).sort_by do |record|
261-
timestamp = record_timestamp(record)
262-
[-timestamp, -record.id] # Always sort DESC (newest first)
263-
end
264-
end
265-
266-
def record_timestamp(record)
267-
if record.is_a?(API::V3::Activities::ActivityEagerLoadingWrapper)
268-
record.created_at&.to_i
269-
elsif record.is_a?(Changeset)
270-
record.committed_on.to_i
271-
end
194+
@paginator, @paginated_journals = WorkPackages::ActivitiesTab::Paginator.paginate(@work_package, params)
272195
end
273196

274197
def respond_with_error(error_message)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# frozen_string_literal: true
2+
3+
#-- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
#++
30+
31+
class WorkPackages::ActivitiesTab::Paginator
32+
include Pagy::Backend
33+
include WorkPackages::ActivitiesTab::JournalSortingInquirable
34+
35+
def self.paginate(work_package, params = {})
36+
new(work_package, params).call
37+
end
38+
39+
def initialize(work_package, params = {})
40+
@work_package = work_package
41+
@params = params
42+
end
43+
44+
def call
45+
anchor_type, target_record_id = extract_target_record_id
46+
47+
pagy, records =
48+
if anchor_type && target_record_id
49+
pagy_array_for_target_journal(anchor_type, target_record_id)
50+
else
51+
pagy_array(base_journals)
52+
end
53+
54+
# For UI display: if user wants "oldest first" UI, reverse the array
55+
records = records.reverse if journal_sorting.asc?
56+
57+
[pagy, records]
58+
end
59+
60+
private
61+
62+
attr_reader :work_package, :params
63+
64+
def extract_target_record_id
65+
anchor = params[:anchor] # e.g., "comment-78758" (without #)
66+
return nil unless anchor
67+
68+
match = anchor.match(/^(comment|activity)-(\d+)$/)
69+
match && match.length == 3 ? [match[1].inquiry, match[2].to_i] : []
70+
end
71+
72+
def pagy_array_for_target_journal(anchor_type, target_record_id)
73+
journals = base_journals
74+
75+
target_index = journals.find_index do |record|
76+
if anchor_type.comment?
77+
record.id == target_record_id
78+
elsif anchor_type.activity?
79+
record.sequence_version == target_record_id
80+
else
81+
false
82+
end
83+
end
84+
85+
if target_index
86+
target_page = (target_index / Pagy::DEFAULT[:limit]) + 1
87+
pagy_array(journals, page: target_page)
88+
else
89+
# Journal might be filtered out or deleted - fallback to page 1
90+
pagy_array(journals, page: 1)
91+
end
92+
end
93+
94+
def base_journals
95+
combine_and_sort_records(fetch_journals, fetch_revisions)
96+
end
97+
98+
def fetch_journals
99+
API::V3::Activities::ActivityEagerLoadingWrapper.wrap(fetch_ar_journals)
100+
end
101+
102+
def fetch_ar_journals
103+
work_package
104+
.journals
105+
.internal_visible
106+
.includes(:user, :customizable_journals, :attachable_journals, :storable_journals, :notifications)
107+
.reorder(version: :desc) # Always fetch newest first for pagination
108+
.with_sequence_version
109+
end
110+
111+
def fetch_revisions
112+
work_package.changesets.includes(:user, :repository)
113+
end
114+
115+
def combine_and_sort_records(journals, revisions)
116+
(journals + revisions).sort_by do |record|
117+
timestamp = record_timestamp(record)
118+
[-timestamp, -record.id] # Always sort DESC (newest first)
119+
end
120+
end
121+
122+
def record_timestamp(record)
123+
if record.is_a?(API::V3::Activities::ActivityEagerLoadingWrapper)
124+
record.created_at&.to_i
125+
elsif record.is_a?(Changeset)
126+
record.committed_on.to_i
127+
end
128+
end
129+
end

spec/factories/work_package_factory.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@
146146
end
147147
end
148148

149-
150149
# force done_ratio in status-based mode if given done_ratio is different from status default
151150
callback(:after_create) do |work_package, evaluator|
152151
next unless WorkPackage.status_based_mode?

0 commit comments

Comments
 (0)