diff --git a/web/src/alert_words_ui.js b/web/src/alert_words_ui.js index 11f6eda9b1..414f992330 100644 --- a/web/src/alert_words_ui.js +++ b/web/src/alert_words_ui.js @@ -29,6 +29,9 @@ export function rerender_alert_words_ui() { }, $parent_container: $("#alert-word-settings"), $simplebar_container: $("#alert-word-settings .progressive-table-wrapper"), + sort_fields: { + ...ListWidget.generic_sort_functions("alphabetic", ["word"]), + }, }); } diff --git a/web/src/attachments_ui.js b/web/src/attachments_ui.js index dc88eacf56..7302002026 100644 --- a/web/src/attachments_ui.js +++ b/web/src/attachments_ui.js @@ -110,10 +110,12 @@ function render_attachments_ui() { }, }, $parent_container: $("#attachments-settings").expectOne(), - init_sort: ["numeric", "create_time"], + init_sort: "create_time_numeric", initially_descending_sort: true, sort_fields: { mentioned_in: sort_mentioned_in, + ...ListWidget.generic_sort_functions("alphabetic", ["name"]), + ...ListWidget.generic_sort_functions("numeric", ["create_time", "size"]), }, $simplebar_container: $("#attachments-settings .progressive-table-wrapper"), }); diff --git a/web/src/list_widget.js b/web/src/list_widget.js index 2940a26f4e..58d24f12b1 100644 --- a/web/src/list_widget.js +++ b/web/src/list_widget.js @@ -92,6 +92,21 @@ export function numeric_sort(prop) { }; } +const generic_sorts = { + alphabetic: alphabetic_sort, + numeric: numeric_sort, +}; + +export function generic_sort_functions(generic_func, props) { + const sorting_functions = {}; + for (const prop of props) { + const key = `${prop}_${generic_func}`; + sorting_functions[key] = generic_sorts[generic_func](prop); + } + + return sorting_functions; +} + export function valid_filter_opts(opts) { if (!opts.filter) { return true; @@ -146,10 +161,6 @@ export function create($container, list, opts) { const meta = { sorting_function: null, sorting_functions: new Map(), - generic_sorting_functions: { - alphabetic: alphabetic_sort, - numeric: numeric_sort, - }, offset: 0, list, filtered_list: list, @@ -281,18 +292,20 @@ export function create($container, list, opts) { meta.reverse_mode = reverse_mode; }, - // the sorting function is either the function or string that calls the - // function to sort the list by. The prop is used for generic functions - // that can be called to sort with a particular prop. - set_sorting_function(sorting_function, prop) { + // the sorting function is either the function or a string which will be a key + // for the sorting_functions map to get the function. In case of generic sort + // functions like numeric and alphabetic, we pass the string in the given format - + // "{property}_{numeric|alphabetic}" - e.g. "email_alphabetic" or "age_numeric". + set_sorting_function(sorting_function) { if (typeof sorting_function === "function") { meta.sorting_function = sorting_function; } else if (typeof sorting_function === "string") { - if (typeof prop === "string") { - meta.sorting_function = meta.generic_sorting_functions[sorting_function](prop); - } else { - meta.sorting_function = meta.sorting_functions.get(sorting_function); + if (!meta.sorting_functions.has(sorting_function)) { + blueslip.error("Sorting function not found: " + sorting_function); + return; } + + meta.sorting_function = meta.sorting_functions.get(sorting_function); } }, @@ -407,7 +420,8 @@ export function create($container, list, opts) { }, sort(sorting_function, prop) { - widget.set_sorting_function(sorting_function, prop); + const key = prop ? `${prop}_${sorting_function}` : sorting_function; + widget.set_sorting_function(key); widget.hard_redraw(); }, @@ -431,7 +445,7 @@ export function create($container, list, opts) { } if (opts.init_sort) { - widget.set_sorting_function(...opts.init_sort); + widget.set_sorting_function(opts.init_sort); } if (opts.initially_descending_sort) { @@ -483,7 +497,7 @@ export function handle_sort($th, list) { list.set_reverse_mode($th.hasClass("descend")); - // if `prop_name` is defined, it will trigger the generic codepath, + // if `prop_name` is defined, it will trigger the generic sort functions, // and not if it is undefined. list.sort(sort_type, prop_name); } diff --git a/web/src/recent_topics_ui.js b/web/src/recent_topics_ui.js index 148ee9f99e..680bfba04b 100644 --- a/web/src/recent_topics_ui.js +++ b/web/src/recent_topics_ui.js @@ -858,6 +858,7 @@ export function complete_rerender() { sort_fields: { stream_sort, topic_sort, + ...ListWidget.generic_sort_functions("numeric", ["last_msg_id"]), }, html_selector: get_topic_row, $simplebar_container: $("#recent_topics_table .table_fix_head"), diff --git a/web/src/settings_emoji.js b/web/src/settings_emoji.js index 7b3d2d2b6d..7dd8386335 100644 --- a/web/src/settings_emoji.js +++ b/web/src/settings_emoji.js @@ -138,8 +138,9 @@ export function populate_emoji() { $parent_container: $("#emoji-settings").expectOne(), sort_fields: { author_full_name: sort_author_full_name, + ...ListWidget.generic_sort_functions("alphabetic", ["name"]), }, - init_sort: ["alphabetic", "name"], + init_sort: "name_alphabetic", $simplebar_container: $("#emoji-settings .progressive-table-wrapper"), }); diff --git a/web/src/settings_exports.js b/web/src/settings_exports.js index 54326c111b..0c5fe785f8 100644 --- a/web/src/settings_exports.js +++ b/web/src/settings_exports.js @@ -83,9 +83,10 @@ export function populate_exports_table(exports) { }, }, $parent_container: $("#data-exports").expectOne(), - init_sort: [sort_user], + init_sort: sort_user, sort_fields: { user: sort_user, + ...ListWidget.generic_sort_functions("numeric", ["export_time"]), }, $simplebar_container: $("#data-exports .progressive-table-wrapper"), }); diff --git a/web/src/settings_invites.js b/web/src/settings_invites.js index 77f28ac4f3..8fd10ac305 100644 --- a/web/src/settings_invites.js +++ b/web/src/settings_invites.js @@ -90,9 +90,15 @@ function populate_invites(invites_data) { }, }, $parent_container: $("#admin-invites-list").expectOne(), - init_sort: [sort_invitee], + init_sort: sort_invitee, sort_fields: { invitee: sort_invitee, + ...ListWidget.generic_sort_functions("alphabetic", ["ref"]), + ...ListWidget.generic_sort_functions("numeric", [ + "invited", + "expires_at", + "invited_as", + ]), }, $simplebar_container: $("#admin-invites-list .progressive-table-wrapper"), }); diff --git a/web/src/settings_linkifiers.js b/web/src/settings_linkifiers.js index 2c34f6e299..dc87b76788 100644 --- a/web/src/settings_linkifiers.js +++ b/web/src/settings_linkifiers.js @@ -160,7 +160,7 @@ export function populate_linkifiers(linkifiers_data) { }, }, $parent_container: $("#linkifier-settings").expectOne(), - init_sort: [sort_pattern], + init_sort: sort_pattern, sort_fields: { pattern: sort_pattern, url: sort_url, diff --git a/web/src/settings_muted_users.js b/web/src/settings_muted_users.js index 69b47b49c7..76d9bba362 100644 --- a/web/src/settings_muted_users.js +++ b/web/src/settings_muted_users.js @@ -36,6 +36,10 @@ export function populate_list() { ); }, }, + sort_fields: { + ...ListWidget.generic_sort_functions("alphabetic", ["user_name"]), + ...ListWidget.generic_sort_functions("numeric", ["date_muted"]), + }, $parent_container: $("#muted-user-settings"), $simplebar_container: $("#muted-user-settings .progressive-table-wrapper"), }); diff --git a/web/src/settings_playgrounds.js b/web/src/settings_playgrounds.js index 5fabd9ab49..10f9847d36 100644 --- a/web/src/settings_playgrounds.js +++ b/web/src/settings_playgrounds.js @@ -60,7 +60,14 @@ export function populate_playgrounds(playgrounds_data) { }, }, $parent_container: $("#playground-settings").expectOne(), - init_sort: ["alphabetic", "pygments_language"], + init_sort: "pygments_language_alphabetic", + sort_fields: { + ...ListWidget.generic_sort_functions("alphabetic", [ + "pygments_language", + "name", + "url_prefix", + ]), + }, $simplebar_container: $("#playground-settings .progressive-table-wrapper"), }); } diff --git a/web/src/settings_streams.js b/web/src/settings_streams.js index ffc225dca5..5ff78fbc9b 100644 --- a/web/src/settings_streams.js +++ b/web/src/settings_streams.js @@ -125,7 +125,10 @@ export function build_default_stream_table() { }, }, $parent_container: $("#admin-default-streams-list").expectOne(), - init_sort: ["alphabetic", "name"], + init_sort: "name_alphabetic", + sort_fields: { + ...ListWidget.generic_sort_functions("alphabetic", ["name"]), + }, $simplebar_container: $("#admin-default-streams-list .progressive-table-wrapper"), }); diff --git a/web/src/settings_user_topics.js b/web/src/settings_user_topics.js index f620586f1d..521f3517ae 100644 --- a/web/src/settings_user_topics.js +++ b/web/src/settings_user_topics.js @@ -45,7 +45,11 @@ export function populate_list() { ); }, }, - init_sort: ["numeric", "date_updated"], + init_sort: "date_updated_numeric", + sort_fields: { + ...ListWidget.generic_sort_functions("alphabetic", ["stream", "topic"]), + ...ListWidget.generic_sort_functions("numeric", ["date_updated", "visibility_policy"]), + }, initially_descending_sort: true, $parent_container: $("#user-topic-settings"), $simplebar_container: $("#user-topic-settings .progressive-table-wrapper"), diff --git a/web/src/settings_users.js b/web/src/settings_users.js index 651df32db5..008597e464 100644 --- a/web/src/settings_users.js +++ b/web/src/settings_users.js @@ -302,11 +302,12 @@ section.bots.create_table = () => { onupdate: reset_scrollbar($bots_table), }, $parent_container: $("#admin-bot-list").expectOne(), - init_sort: ["alphabetic", "full_name"], + init_sort: "full_name_alphabetic", sort_fields: { email: sort_bot_email, bot_owner: sort_bot_owner, role: sort_role, + ...ListWidget.generic_sort_functions("alphabetic", ["full_name", "bot_type"]), }, $simplebar_container: $("#admin-bot-list .progressive-table-wrapper"), }); @@ -330,12 +331,13 @@ section.active.create_table = (active_users) => { onupdate: reset_scrollbar($users_table), }, $parent_container: $("#admin-user-list").expectOne(), - init_sort: ["alphabetic", "full_name"], + init_sort: "full_name_alphabetic", sort_fields: { email: sort_email, last_active: sort_last_active, role: sort_role, id: sort_user_id, + ...ListWidget.generic_sort_functions("alphabetic", ["full_name"]), }, $simplebar_container: $("#admin-user-list .progressive-table-wrapper"), }); @@ -359,11 +361,12 @@ section.deactivated.create_table = (deactivated_users) => { onupdate: reset_scrollbar($deactivated_users_table), }, $parent_container: $("#admin-deactivated-users-list").expectOne(), - init_sort: ["alphabetic", "full_name"], + init_sort: "full_name_alphabetic", sort_fields: { email: sort_email, role: sort_role, id: sort_user_id, + ...ListWidget.generic_sort_functions("alphabetic", ["full_name"]), }, $simplebar_container: $("#admin-deactivated-users-list .progressive-table-wrapper"), }); diff --git a/web/src/stream_create_subscribers.js b/web/src/stream_create_subscribers.js index ebdd410954..629a69cc32 100644 --- a/web/src/stream_create_subscribers.js +++ b/web/src/stream_create_subscribers.js @@ -99,6 +99,7 @@ export function build_widgets() { sort_fields: { email: settings_users.sort_email, id: settings_users.sort_user_id, + ...ListWidget.generic_sort_functions("alphabetic", ["full_name"]), }, filter: { $element: $("#people_to_add .add-user-list-filter"), diff --git a/web/src/stream_edit_subscribers.js b/web/src/stream_edit_subscribers.js index b3cecdeaf1..b97cfac696 100644 --- a/web/src/stream_edit_subscribers.js +++ b/web/src/stream_edit_subscribers.js @@ -129,6 +129,7 @@ function make_list_widget({$parent_container, name, user_ids, user_can_remove_su sort_fields: { email: settings_users.sort_email, id: settings_users.sort_user_id, + ...ListWidget.generic_sort_functions("alphabetic", ["full_name"]), }, $simplebar_container, }); diff --git a/web/src/user_group_create_members.js b/web/src/user_group_create_members.js index bd0d213b33..591252eeb8 100644 --- a/web/src/user_group_create_members.js +++ b/web/src/user_group_create_members.js @@ -97,6 +97,7 @@ export function build_widgets() { sort_fields: { email: settings_users.sort_email, id: settings_users.sort_user_id, + ...ListWidget.generic_sort_functions("alphabetic", ["full_name"]), }, modifier(user) { const item = { diff --git a/web/src/user_group_edit_members.js b/web/src/user_group_edit_members.js index f5f11e9924..5bb674f1de 100644 --- a/web/src/user_group_edit_members.js +++ b/web/src/user_group_edit_members.js @@ -71,6 +71,7 @@ function make_list_widget({$parent_container, name, user_ids}) { sort_fields: { email: settings_users.sort_email, id: settings_users.sort_user_id, + ...ListWidget.generic_sort_functions("alphabetic", ["full_name"]), }, modifier(item) { return format_member_list_elem(item); diff --git a/web/tests/alert_words_ui.test.js b/web/tests/alert_words_ui.test.js index dbd00e3c76..976aaa10f3 100644 --- a/web/tests/alert_words_ui.test.js +++ b/web/tests/alert_words_ui.test.js @@ -31,6 +31,7 @@ run_test("rerender_alert_words_ui", ({mock_template}) => { list_widget_create_called = true; return alert_words; }, + generic_sort_functions: noop, }); mock_template("settings/alert_word_settings_item.hbs", false, (args) => { assert.ok(["foo", "bar"].includes(args.alert_word.word)); diff --git a/web/tests/list_widget.test.js b/web/tests/list_widget.test.js index 73ade655f8..a44c3e0169 100644 --- a/web/tests/list_widget.test.js +++ b/web/tests/list_widget.test.js @@ -401,6 +401,10 @@ run_test("sorting", () => { filter: { predicate: () => true, }, + sort_fields: { + ...ListWidget.generic_sort_functions("alphabetic", ["name"]), + ...ListWidget.generic_sort_functions("numeric", ["salary"]), + }, $simplebar_container: $scroll_container, }; @@ -503,7 +507,7 @@ run_test("custom sort", () => { product: sort_by_product, x_value: sort_by_x, }, - init_sort: [sort_by_product], + init_sort: sort_by_product, $simplebar_container: $scroll_container, }); diff --git a/web/tests/recent_topics.test.js b/web/tests/recent_topics.test.js index a68fa111c1..250721d02f 100644 --- a/web/tests/recent_topics.test.js +++ b/web/tests/recent_topics.test.js @@ -43,7 +43,7 @@ let expected_data_to_replace_in_list_widget; const ListWidget = mock_esm("../src/list_widget", { modifier: noop, - + generic_sort_functions: noop, create(_container, mapped_topic_values, opts) { const formatted_topics = []; ListWidget.modifier = opts.modifier; diff --git a/web/tests/settings_muted_users.test.js b/web/tests/settings_muted_users.test.js index fa620fca17..729f01fde9 100644 --- a/web/tests/settings_muted_users.test.js +++ b/web/tests/settings_muted_users.test.js @@ -6,15 +6,17 @@ const {mock_esm, zrequire} = require("./lib/namespace"); const {run_test} = require("./lib/test"); const $ = require("./lib/zjquery"); -const list_widget = mock_esm("../src/list_widget"); +const noop = () => {}; + +const list_widget = mock_esm("../src/list_widget", { + generic_sort_functions: noop, +}); const muted_users_ui = mock_esm("../src/muted_users_ui"); const settings_muted_users = zrequire("settings_muted_users"); const muted_users = zrequire("muted_users"); const people = zrequire("people"); -const noop = () => {}; - run_test("settings", ({override}) => { people.add_active_user({user_id: 5, email: "five@zulip.com", full_name: "Feivel Fiverson"}); muted_users.add_muted_user(5, 1577836800); diff --git a/web/tests/settings_user_topics.test.js b/web/tests/settings_user_topics.test.js index 9ea1fb56ad..3d12ed1870 100644 --- a/web/tests/settings_user_topics.test.js +++ b/web/tests/settings_user_topics.test.js @@ -6,14 +6,16 @@ const {mock_esm, zrequire} = require("./lib/namespace"); const {run_test} = require("./lib/test"); const $ = require("./lib/zjquery"); -const list_widget = mock_esm("../src/list_widget"); +const noop = () => {}; + +const list_widget = mock_esm("../src/list_widget", { + generic_sort_functions: noop, +}); const settings_user_topics = zrequire("settings_user_topics"); const stream_data = zrequire("stream_data"); const user_topics = zrequire("user_topics"); -const noop = () => {}; - const frontend = { stream_id: 101, name: "frontend",