groups: sort the nameless groups last
A group with no name is shown by its id, and a list of bare ids
would bury the named groups in noise, so the nameless ones go last.
They still sort after the sync: groups, and in the import picker
they come after every named group, however many members they have.
Assisted-by: Claude Opus 5.5 via Claude Code
diff --git a/lib/pro_tacts/admin/import_groups.rb b/lib/pro_tacts/admin/import_groups.rb
index 76e1231..b30ede3 100644
--- a/lib/pro_tacts/admin/import_groups.rb
+++ b/lib/pro_tacts/admin/import_groups.rb
@@ -44,8 +44,9 @@ module ProTacts
# @rbs @capped: Array[String]
# @rbs @was: Array[String]
- # The `sync:` groups lead (GroupChoice#<=>), then largest first
- # and alphabetical among groups the same size: the groups an
+ # The `sync:` groups lead and the nameless ones trail
+ # (GroupChoice#<=>), then largest first and alphabetical among
+ # groups the same size: the groups an
# arrival is likeliest to belong in are the ones most of the
# book already does, so they are the rows the cap leaves
# standing, and the filter reaches the rest. The groups dialog
@@ -73,7 +74,7 @@ module ProTacts
# what was toggled (Web#apply_groups). A new contact is in none.
#: (groups: Array[Store::GroupChoice], joined: Array[String], named: Array[String], ?was: Array[String]) -> void
def initialize(groups:, joined:, named:, was: [])
- @groups = groups.sort_by { [it.sync? ? 0 : 1, -it.member_count, it.label.downcase] }
+ @groups = groups.sort_by { [it.sync? ? 0 : 1, it.name.nil? ? 1 : 0, -it.member_count, it.label.downcase] }
@joined = joined
@named = named
@was = was
diff --git a/lib/pro_tacts/group.rb b/lib/pro_tacts/group.rb
index 43c7104..ca8e6c1 100644
--- a/lib/pro_tacts/group.rb
+++ b/lib/pro_tacts/group.rb
@@ -51,13 +51,15 @@ module ProTacts
# The order every listing of groups reads in: the `sync:` groups
# — the books a client syncs, the store's own rather than
- # anything a person made — first, then the rest alphabetically.
+ # anything a person made — first, then the rest alphabetically,
+ # and the nameless ones last, their ids being no names to read.
# GroupChoice spells this again for the pickers; keep the two
# alike. Comparable deliberately left out: its `==` would sit in
# front of Data's whole-value one, and `sort` asks only for `<=>`.
#: (Group other) -> Integer?
def <=>(other)
- [sync? ? 0 : 1, label.downcase, id] <=> [other.sync? ? 0 : 1, other.label.downcase, other.id]
+ [sync? ? 0 : 1, name.nil? ? 1 : 0, label.downcase, id] <=>
+ [other.sync? ? 0 : 1, other.name.nil? ? 1 : 0, other.label.downcase, other.id]
end
# The group's etag, for the editor's snapshot guard: a hash of
diff --git a/lib/pro_tacts/store.rb b/lib/pro_tacts/store.rb
index 820db76..f55814d 100644
--- a/lib/pro_tacts/store.rb
+++ b/lib/pro_tacts/store.rb
@@ -126,7 +126,8 @@ module ProTacts
# choices; keep the two alike.
#: (GroupChoice other) -> Integer?
def <=>(other)
- [sync? ? 0 : 1, label.downcase, id] <=> [other.sync? ? 0 : 1, other.label.downcase, other.id]
+ [sync? ? 0 : 1, name.nil? ? 1 : 0, label.downcase, id] <=>
+ [other.sync? ? 0 : 1, other.name.nil? ? 1 : 0, other.label.downcase, other.id]
end
end
diff --git a/test/pro_tacts/admin/test_groups_pages.rb b/test/pro_tacts/admin/test_groups_pages.rb
index 9aa0408..40f896f 100644
--- a/test/pro_tacts/admin/test_groups_pages.rb
+++ b/test/pro_tacts/admin/test_groups_pages.rb
@@ -65,6 +65,21 @@ class AdminGroupsPagesTest < Minitest::Test
end
end
+ # A nameless group is labelled by its id, which reads as noise among
+ # names, so those sink below the named ones.
+ def test_the_list_ends_with_the_nameless_groups
+ with_contacts({}) do |store|
+ nameless = store.create_group
+ zoetrope = store.create_group(name: "zoetrope")
+ abacus = store.create_group(name: "Abacus")
+
+ get "/groups"
+
+ assert_equal [abacus, zoetrope, nameless],
+ last_response.body.scan(%r{<a href="/groups/([^"]+)">}).flatten
+ end
+ end
+
def test_an_empty_list_says_so
with_contacts({}) do
get "/groups"
diff --git a/test/pro_tacts/admin/test_import_pages.rb b/test/pro_tacts/admin/test_import_pages.rb
index 6979833..e9f2b93 100644
--- a/test/pro_tacts/admin/test_import_pages.rb
+++ b/test/pro_tacts/admin/test_import_pages.rb
@@ -820,6 +820,23 @@ class AdminImportPagesTest < Minitest::Test
end
end
+ # However many it holds: a nameless group sinks here as it does in
+ # the listings.
+ def test_the_groups_beside_a_card_end_with_the_nameless_groups
+ with_contacts({"jane" => JANE, "sam" => PLAIN}) do |store|
+ nameless = store.create_group
+ store.add_member(nameless, "jane")
+ store.add_member(nameless, "sam")
+ small = store.create_group(name: "small")
+ id = upload(JANE)
+
+ get "/import/#{id}/0"
+
+ assert_equal [small, nameless],
+ last_response.body.scan(/name="groups\[\]" value="([^"]+)"/).flatten
+ end
+ end
+
def test_the_groups_beside_a_card_offer_no_such_thing_uncapped
with_contacts({}) do |store|
# Seven, the import's own name being the eighth row.