mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 05:53:43 +00:00
recent_conversations: Improve sorting behavior.
Refine sorting algorithm of recent conversation list so that it works like spreadsheet sorting, with previous sorting orders remembered. Fixes #33289.
This commit is contained in:
@@ -11,6 +11,7 @@ type SortingFunction<T> = (a: T, b: T) => number;
|
|||||||
|
|
||||||
type ListWidgetMeta<Key, Item = Key> = {
|
type ListWidgetMeta<Key, Item = Key> = {
|
||||||
sorting_function: SortingFunction<Item> | null;
|
sorting_function: SortingFunction<Item> | null;
|
||||||
|
applied_sorting_functions: [SortingFunction<Item>, boolean][]; // This is used to keep track of the sorting functions applied.
|
||||||
sorting_functions: Map<string, SortingFunction<Item>>;
|
sorting_functions: Map<string, SortingFunction<Item>>;
|
||||||
filter_value: string;
|
filter_value: string;
|
||||||
offset: number;
|
offset: number;
|
||||||
@@ -280,6 +281,7 @@ export function create<Key, Item = Key>(
|
|||||||
|
|
||||||
const meta: ListWidgetMeta<Key, Item> = {
|
const meta: ListWidgetMeta<Key, Item> = {
|
||||||
sorting_function: null,
|
sorting_function: null,
|
||||||
|
applied_sorting_functions: [],
|
||||||
sorting_functions: new Map(),
|
sorting_functions: new Map(),
|
||||||
offset: 0,
|
offset: 0,
|
||||||
list,
|
list,
|
||||||
@@ -303,11 +305,26 @@ export function create<Key, Item = Key>(
|
|||||||
meta.filtered_list = get_filtered_items(meta.filter_value, meta.list, opts);
|
meta.filtered_list = get_filtered_items(meta.filter_value, meta.list, opts);
|
||||||
|
|
||||||
if (meta.sorting_function) {
|
if (meta.sorting_function) {
|
||||||
meta.filtered_list.sort(meta.sorting_function);
|
// If the sorting function is already applied, remove it to avoid duplicate sorting.
|
||||||
}
|
const existing_sorting_function_index = meta.applied_sorting_functions.findIndex(
|
||||||
|
([sorting_function, _]) => sorting_function === meta.sorting_function,
|
||||||
|
);
|
||||||
|
if (existing_sorting_function_index !== -1) {
|
||||||
|
meta.applied_sorting_functions.splice(existing_sorting_function_index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (meta.reverse_mode) {
|
meta.applied_sorting_functions.push([meta.sorting_function, meta.reverse_mode]);
|
||||||
meta.filtered_list.reverse();
|
meta.filtered_list.sort((a, b) => {
|
||||||
|
for (let i = meta.applied_sorting_functions.length - 1; i >= 0; i -= 1) {
|
||||||
|
const sorting_function = meta.applied_sorting_functions[i]![0];
|
||||||
|
const is_reverse = meta.applied_sorting_functions[i]![1];
|
||||||
|
const result = sorting_function(a, b);
|
||||||
|
if (result !== 0) {
|
||||||
|
return is_reverse ? -result : result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -527,6 +527,105 @@ run_test("sorting", () => {
|
|||||||
assert.ok(cleared);
|
assert.ok(cleared);
|
||||||
expected_html = html_for([ellen, alice, bob, cal, dave]);
|
expected_html = html_for([ellen, alice, bob, cal, dave]);
|
||||||
assert.deepEqual($container.$appended_data.html(), expected_html);
|
assert.deepEqual($container.$appended_data.html(), expected_html);
|
||||||
|
});
|
||||||
|
|
||||||
|
run_test("Apply consecutive sorts", () => {
|
||||||
|
const $container = make_container();
|
||||||
|
const $scroll_container = make_scroll_container();
|
||||||
|
const $sort_container = make_sort_container();
|
||||||
|
|
||||||
|
let cleared;
|
||||||
|
$container.empty = () => {
|
||||||
|
cleared = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const alice = {name: "alice", salary: 50};
|
||||||
|
const bob = {name: "bob", salary: 40};
|
||||||
|
const cal = {name: "cal", salary: 30};
|
||||||
|
const dave = {name: "dave", salary: 20};
|
||||||
|
const ellen = {name: "ellen", salary: 95};
|
||||||
|
const bob_2 = {name: "bob", salary: 20};
|
||||||
|
const cal_2 = {name: "cal", salary: 60};
|
||||||
|
|
||||||
|
const list = [alice, bob, cal, dave, ellen, bob_2, cal_2];
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
name: "sorting-list",
|
||||||
|
$parent_container: $sort_container,
|
||||||
|
modifier_html: (item) => div(item.name) + div(item.salary),
|
||||||
|
get_item: (item) => item,
|
||||||
|
filter: {
|
||||||
|
predicate: () => true,
|
||||||
|
},
|
||||||
|
sort_fields: {
|
||||||
|
...ListWidget.generic_sort_functions("alphabetic", ["name"]),
|
||||||
|
...ListWidget.generic_sort_functions("numeric", ["salary"]),
|
||||||
|
},
|
||||||
|
$simplebar_container: $scroll_container,
|
||||||
|
};
|
||||||
|
|
||||||
|
function html_for(people) {
|
||||||
|
return people.map((item) => opts.modifier_html(item)).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
ListWidget.create($container, list, opts);
|
||||||
|
|
||||||
|
let button_opts;
|
||||||
|
let $button;
|
||||||
|
let expected_html;
|
||||||
|
|
||||||
|
// Apply sorting by salary first, then by name
|
||||||
|
button_opts = {
|
||||||
|
sort_type: "numeric",
|
||||||
|
prop_name: "salary",
|
||||||
|
list_name: "my-list",
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
$button = sort_button(button_opts);
|
||||||
|
|
||||||
|
$sort_container.f.apply($button);
|
||||||
|
|
||||||
|
button_opts = {
|
||||||
|
sort_type: "alphabetic",
|
||||||
|
prop_name: "name",
|
||||||
|
list_name: "my-list",
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
$button = sort_button(button_opts);
|
||||||
|
|
||||||
|
$sort_container.f.apply($button);
|
||||||
|
|
||||||
|
assert.ok(cleared);
|
||||||
|
assert.ok($button.siblings_deactivated);
|
||||||
|
|
||||||
|
expected_html = html_for([alice, bob_2, bob, cal, cal_2, dave, ellen]);
|
||||||
|
assert.deepEqual($container.$appended_data.html(), expected_html);
|
||||||
|
|
||||||
|
// Apply sorting by salary again, the previous sort by salary should be removed
|
||||||
|
button_opts = {
|
||||||
|
sort_type: "numeric",
|
||||||
|
prop_name: "salary",
|
||||||
|
list_name: "my-list",
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
$button = sort_button(button_opts);
|
||||||
|
|
||||||
|
cleared = false;
|
||||||
|
$sort_container.f.apply($button);
|
||||||
|
assert.ok(cleared);
|
||||||
|
expected_html = html_for([bob_2, dave, cal, bob, alice, cal_2, ellen]);
|
||||||
|
assert.deepEqual($container.$appended_data.html(), expected_html);
|
||||||
|
assert.ok(!$button.hasClass("descend"));
|
||||||
|
|
||||||
|
// Hit the salary field again to reverse the salary sorting
|
||||||
|
cleared = false;
|
||||||
|
$sort_container.f.apply($button);
|
||||||
|
assert.ok(cleared);
|
||||||
|
expected_html = html_for([ellen, cal_2, alice, bob, cal, bob_2, dave]);
|
||||||
|
assert.deepEqual($container.$appended_data.html(), expected_html);
|
||||||
assert.ok($button.hasClass("descend"));
|
assert.ok($button.hasClass("descend"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user