Skip to content

Commit c42a55c

Browse files
committed
don't sort pinned items first in lists
1 parent 60c355a commit c42a55c

File tree

8 files changed

+134
-32
lines changed

8 files changed

+134
-32
lines changed

app/helpers/application_helper.rb

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,57 @@ def combine_and_sort_gallery_images(regular_images, basil_images)
7373
}
7474
end
7575

76-
# Sort with consistent criteria
76+
# Sort with consistent criteria - using only position and creation date
77+
# This allows users to order images freely, including pinned images
7778
combined_images.sort_by do |img|
78-
# First by pinned status - pin always takes precedence
79-
pinned_sort = (img[:data].respond_to?(:pinned?) && img[:data].pinned?) ? 0 : 1
80-
81-
# Then by position - using presence check for nil/blank values
79+
# First by position - using presence check for nil/blank values
8280
position_value = if img[:data].respond_to?(:position) && img[:data].position.present?
8381
img[:data].position
8482
else
8583
999999
8684
end
8785

88-
# Finally by created date as tertiary sort with fallback
86+
# Then by created date as secondary sort with fallback
8987
created_at = img[:created_at] || Time.current
9088

9189
# Add a unique identifier to ensure stable sorting
9290
unique_id = "#{img[:type]}-#{img[:id]}"
9391

94-
# Return sort keys array for stable sorting
95-
[pinned_sort, position_value, created_at, unique_id]
92+
# Return sort keys array for stable sorting - no longer sorting by pinned status
93+
[position_value, created_at, unique_id]
94+
end
95+
end
96+
97+
# Gets the best image to use for a preview (card/header) by prioritizing pinned images
98+
# @param regular_images [Array<ImageUpload>] regular image uploads
99+
# @param basil_images [Array<BasilCommission>] AI-generated images
100+
# @return [Hash] The best image to use as a preview (pinned if available)
101+
def get_preview_image(regular_images, basil_images)
102+
# First look for pinned images
103+
pinned_regular = regular_images.find { |img| img.respond_to?(:pinned?) && img.pinned? }
104+
pinned_basil = basil_images.find { |img| img.respond_to?(:pinned?) && img.pinned? }
105+
106+
# Use the first pinned image found (prioritize regular uploads if both exist)
107+
if pinned_regular.present?
108+
return {
109+
id: pinned_regular.id,
110+
type: 'image_upload',
111+
data: pinned_regular,
112+
created_at: pinned_regular.created_at
113+
}
114+
elsif pinned_basil.present?
115+
return {
116+
id: pinned_basil.id,
117+
type: 'basil_commission',
118+
data: pinned_basil,
119+
created_at: pinned_basil.saved_at
120+
}
96121
end
122+
123+
# If no pinned images, get all images
124+
combined = combine_and_sort_gallery_images(regular_images, basil_images)
125+
126+
# Return the first sorted image, or nil if none available
127+
combined.first
97128
end
98129
end

app/models/page_types/content_page.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,24 @@ class ContentPage < ApplicationRecord
99
include Authority::Abilities
1010
self.authorizer_name = 'ContentPageAuthorizer'
1111

12+
# Returns a single image for use in previews/cards, prioritizing pinned images
13+
# This method keeps the original behavior of prioritizing pinned images for thumbnails/previews
1214
def random_image_including_private(format: :small)
15+
# Always prioritize pinned images first for preview cards
1316
pinned_image = ImageUpload.where(content_type: self.page_type, content_id: self.id, pinned: true).first
1417
return pinned_image.src(format) if pinned_image
1518

1619
pinned_commission = BasilCommission.where(entity_type: self.page_type, entity_id: self.id, pinned: true).where.not(saved_at: nil).includes([:image_attachment]).first
1720
return pinned_commission.image if pinned_commission
1821

22+
# Fall back to random images if no pinned images exist
1923
random_image = ImageUpload.where(content_type: self.page_type, content_id: self.id).sample
2024
return random_image.src(format) if random_image
2125

2226
random_commission = BasilCommission.where(entity_type: self.page_type, entity_id: self.id).where.not(saved_at: nil).includes([:image_attachment]).sample
2327
return random_commission.image if random_commission
2428

29+
# Use default image as last resort
2530
ActionController::Base.helpers.asset_path("card-headers/#{self.page_type.downcase.pluralize}.webp")
2631
end
2732

app/views/browse/tag.html.erb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@
1010
</ol>
1111
<p class="grey-text" style="font-size: 14px;">Note: It may take a few minutes for new content to appear.</p>
1212
</div>
13+
<div class="modal-content" style="border-top: 1px solid #e0e0e0; padding-top: 15px;">
14+
<h4><i class="material-icons <%= @accent_color %>-text left" style="margin-right: 10px;">brush</i> What is Art Fight?</h4>
15+
<p>Art Fight is an annual art trading event that runs during July where:</p>
16+
<ul class="browser-default">
17+
<li>Artists are divided into two opposing teams</li>
18+
<li>Participants "attack" the opposing team by drawing their original characters (OCs)</li>
19+
<li>Each completed artwork earns points for your team</li>
20+
<li>The team with the most points at the end of July wins</li>
21+
</ul>
22+
<p>It's a great way to practice your art skills, discover new characters to draw, and connect with other creatives. Adding the <span class="highlight">ArtFight2025</span> tag to your public pages helps other Notebook.ai users find your characters for Art Fight!</p>
23+
<p class="grey-text" style="font-size: 14px;">Note: Notebook.ai is not officially affiliated with Art Fight. Visit <a href="https://artfight.net" target="_blank" class="<%= @accent_color %>-text">artfight.net</a> to learn more and participate.</p>
24+
</div>
1325
<div class="modal-footer">
1426
<a href="#!" class="modal-close waves-effect waves-light <%= @accent_color %> btn-flat white-text">Got it</a>
1527
</div>

app/views/content/display/_image_card_header.html.erb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
end
1313
basil_images = @content.basil_commissions.where.not(saved_at: nil).ordered.to_a
1414

15-
# Use our unified helper to combine and sort all images
16-
combined_images = combine_and_sort_gallery_images(regular_images, basil_images)
15+
# For headers/card previews, use get_preview_image to prioritize pinned images
16+
preview_image = get_preview_image(regular_images, basil_images)
17+
combined_images = preview_image.present? ? [preview_image] : []
1718

1819
# If we have no images, try to add a default image
1920
if combined_images.empty? && @content.respond_to?(:default_image)

app/views/content/form/gallery/_panel.html.erb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
# Calculate total images for display purposes
99
total_images = regular_images.count + basil_images.count
1010

11-
# Use the unified helper to combine and sort images consistently
11+
# Use the unified helper to combine and sort images consistently by position
12+
# This no longer prioritizes pinned images in the sort order
1213
combined_images = combine_and_sort_gallery_images(regular_images, basil_images)
1314
%>
1415

app/views/content/gallery.html.erb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,16 @@
7373
<% else %>
7474
<!-- Gallery grid -->
7575
<%
76-
# Use the unified helper to combine and sort images consistently
76+
# Use the unified helper to combine and sort images consistently by position
77+
# This no longer prioritizes pinned images in the sort order
7778
combined_images = combine_and_sort_gallery_images(@images, @basil_images)
7879
%>
7980

8081
<div class="gallery-grid" id="galleryGrid">
8182
<% combined_images.each_with_index do |image_item, index| %>
8283
<div class="gallery-item">
8384
<div class="gallery-card" data-index="<%= index %>">
85+
<%# image_item[:data].position %>
8486
<% if image_item[:type] == 'image_upload' %>
8587
<%= image_tag image_item[:data].src(:medium), class: "gallery-image",
8688
data: {

test/helpers/application_helper_test.rb

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
class ApplicationHelperTest < ActionView::TestCase
44
include ApplicationHelper
55

6+
# Skip loading fixtures to avoid database issues
7+
self.use_transactional_tests = false
8+
69
# Use standalone test without fixtures for the helper method
7-
test "combine_and_sort_gallery_images should sort correctly" do
10+
test "combine_and_sort_gallery_images should sort by position and creation date" do
811
# Skip test if helper method doesn't exist (for CI runs)
912
skip unless respond_to?(:combine_and_sort_gallery_images)
1013

@@ -23,7 +26,7 @@ def regular_image1.respond_to?(method)
2326
def regular_image2.id; 2; end
2427
def regular_image2.created_at; 1.day.ago; end
2528
def regular_image2.position; 2; end
26-
def regular_image2.pinned?; true; end
29+
def regular_image2.pinned?; true; end # Should be ignored in sorting
2730
def regular_image2.respond_to?(method)
2831
[:id, :created_at, :position, :pinned?].include?(method)
2932
end
@@ -44,11 +47,39 @@ def basil_image.respond_to?(method)
4447
# Assertions
4548
assert_equal 3, result.size
4649

47-
# First image should be the pinned one
48-
assert_equal 2, result[0][:data].id
49-
50-
# Then by position
51-
assert_equal 3, result[1][:data].id # basil with position 1
50+
# First by position (not by pinned status)
51+
assert_equal 3, result[0][:data].id # basil with position 1
52+
assert_equal 2, result[1][:data].id # regular with position 2 (pinned but not first)
5253
assert_equal 1, result[2][:data].id # regular with position 3
5354
end
55+
56+
test "get_preview_image should prioritize pinned images" do
57+
# Skip test if helper method doesn't exist (for CI runs)
58+
skip unless respond_to?(:get_preview_image)
59+
60+
# Create test objects with one pinned image
61+
regular_image1 = Object.new
62+
def regular_image1.id; 1; end
63+
def regular_image1.created_at; 3.days.ago; end
64+
def regular_image1.position; 1; end
65+
def regular_image1.pinned?; false; end
66+
def regular_image1.respond_to?(method)
67+
[:id, :created_at, :position, :pinned?].include?(method)
68+
end
69+
70+
regular_image2 = Object.new
71+
def regular_image2.id; 2; end
72+
def regular_image2.created_at; 1.day.ago; end
73+
def regular_image2.position; 3; end # Bad position, but pinned
74+
def regular_image2.pinned?; true; end
75+
def regular_image2.respond_to?(method)
76+
[:id, :created_at, :position, :pinned?].include?(method)
77+
end
78+
79+
# Test preview image selection
80+
result = get_preview_image([regular_image1, regular_image2], [])
81+
82+
# Pinned image should be chosen for preview
83+
assert_equal 2, result[:data].id
84+
end
5485
end

test/unit/gallery_sorting_test.rb

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ class GallerySortingTest < ActiveSupport::TestCase
44
# Include the helper module directly
55
include ApplicationHelper
66

7+
# Skip loading fixtures to avoid database issues
8+
self.use_transactional_tests = false
9+
710
# Create test classes that mimic needed behavior
811
class MockImageUpload
912
attr_reader :id, :created_at, :position, :pinned
@@ -43,18 +46,20 @@ def respond_to?(method)
4346
end
4447
end
4548

46-
test "combine_and_sort_gallery_images should prioritize pinned images" do
47-
# Create images with different creation dates but pin one
48-
oldest = MockImageUpload.new(id: 1, created_at: 3.days.ago)
49-
pinned = MockImageUpload.new(id: 2, created_at: 2.days.ago, pinned: true)
50-
newest = MockImageUpload.new(id: 3, created_at: 1.day.ago)
49+
test "combine_and_sort_gallery_images should sort by position, not pinned status" do
50+
# Create images with different creation dates including a pinned one
51+
oldest = MockImageUpload.new(id: 1, created_at: 3.days.ago, position: 3)
52+
pinned = MockImageUpload.new(id: 2, created_at: 2.days.ago, pinned: true, position: 2)
53+
newest = MockImageUpload.new(id: 3, created_at: 1.day.ago, position: 1)
5154

5255
# Call helper method
5356
result = combine_and_sort_gallery_images([oldest, pinned, newest], [])
5457

55-
# Verify the pinned image comes first despite not being newest
58+
# Verify sorted by position, not pinned status
5659
assert_equal 3, result.size
57-
assert_equal 2, result[0][:data].id # Pinned should be first
60+
assert_equal 3, result[0][:data].id # Position 1 should be first
61+
assert_equal 2, result[1][:data].id # Position 2 should be second, even though pinned
62+
assert_equal 1, result[2][:data].id # Position 3 should be third
5863
end
5964

6065
test "combine_and_sort_gallery_images should respect position values" do
@@ -92,16 +97,30 @@ def respond_to?(method)
9297
assert_equal 1, result[2][:data].id # position 3
9398
end
9499

95-
test "combine_and_sort_gallery_images should prioritize pinned over position" do
96-
# One pinned but with higher position value
100+
test "get_preview_image should prioritize pinned images" do
101+
# Create images with one of them pinned
97102
image1 = MockImageUpload.new(id: 1, created_at: 3.days.ago, position: 1)
98103
image2 = MockImageUpload.new(id: 2, created_at: 2.days.ago, position: 2, pinned: true)
104+
image3 = MockImageUpload.new(id: 3, created_at: 1.day.ago, position: 3)
105+
106+
# Call helper method
107+
result = get_preview_image([image1, image2, image3], [])
108+
109+
# Verify the pinned image is returned
110+
assert_equal 2, result[:data].id # Pinned should be selected
111+
assert result[:data].pinned?
112+
end
113+
114+
test "get_preview_image should fall back to first sorted image when no pinned images exist" do
115+
# Images with no pinned status
116+
image1 = MockImageUpload.new(id: 1, created_at: 3.days.ago, position: 3)
117+
image2 = MockImageUpload.new(id: 2, created_at: 2.days.ago, position: 1)
118+
image3 = MockImageUpload.new(id: 3, created_at: 1.day.ago, position: 2)
99119

100120
# Call helper method
101-
result = combine_and_sort_gallery_images([image1, image2], [])
121+
result = get_preview_image([image1, image2, image3], [])
102122

103-
# Verify the pinned image comes first despite higher position
104-
assert_equal 2, result[0][:data].id # Pinned should be first
105-
assert_equal 1, result[1][:data].id # Non-pinned second
123+
# Verify returns the first image by position
124+
assert_equal 2, result[:data].id # Position 1 should be selected
106125
end
107126
end

0 commit comments

Comments
 (0)