-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fall back to sorting by name on ties #535
Fall back to sorting by name on ties #535
Conversation
4954c81
to
5a4b0c4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
I think the debian fail is relevant, so probably don't merge this yet... |
Seems so! I'll download and take a look |
So on debian testing the ordering on size is correct except for the first directory:
Should be
In fedora VM
So we first so a Btrfs
Debian
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a fix for sorting on directories as they are compared by size when they are both a directory.
5a4b0c4
to
91df53e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ironically this fails on typescript checks :-)
@@ -423,7 +444,7 @@ const Row = React.memo(function Item({ file, isSelected }) { | |||
dataLabel="date" | |||
modifier="nowrap" | |||
> | |||
{timeformat.dateTime(file.mtime * 1000)} | |||
{file.mtime ? timeformat.dateTime(file.mtime * 1000) : null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
null would show nothing, that's probably fine. Wondering if we might want to show no date
.
@@ -354,6 +354,11 @@ class TestFiles(testlib.MachineCase): | |||
|
|||
self.enter_files() | |||
|
|||
# set a bogus sort value in localStorage to make sure we handle it gracefully | |||
b.eval_js("""window.localStorage.setItem("files:sort", 'bzzt')""") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐝
91df53e
to
2f0f249
Compare
The pixel test fails are kinda interesting. Sometimes the |
2f0f249
to
5b0ef42
Compare
The pixel failures on |
src/fileActions.jsx
Outdated
@@ -474,7 +474,7 @@ export const fileActions = (path, selected, setSelected, clipboard, setClipboard | |||
); | |||
}, | |||
}, | |||
{ type: "divider" }, | |||
null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels super non-obvious. null
doesn't give me a hint that we are rendering a divider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. I brought back { type: "divider" }
with a bit of discriminated union magic (and a bonus of some extra code in typescript for my efforts).
5b0ef42
to
779a7ae
Compare
This was trivial.
The following commit will port files-card-body to TypeScript, revealing several safety issues with the menu items handling code. Fix them up before we do that: - it's difficult to reason about the types of menu items when we have two similar but different things: items and dividers. Instead of using { type: "divider" } as a placeholder for a divider, we could use null, but this would have a negative impact on readabilty. Fortunately, with explicit typing we can form what is essentially discriminated union. Move the menu code to its own TypeScript file, continuing the shrinking of fileActions.jsx, and add a type for menu items. Now TypeScript can reason about the properties that are present on all non-divider items (`title` and `onClick`). This prevents TypeScript from complaining that `onClick` might be `undefined`, for example. The explicit typing also means that if we make a mistake when defining a menu item, we'll see the type error directly at the point of the mistake. - className is only defined on some items, but we do an unconditional concatenation of `"context-menu-option " + item.className`. That means that we set the class to `context-menu-option undefined`, which is definitely not what we wanted - strictly speaking we're not allowed to pass isDisabled={undefined}, but we're doing that for most items. Provide a default of false. - not really a type issue, but strange: we're using a translated string as a key= on our <MenuItem/> elements when we have a unique ID. Use the id instead, which is what the sidebar does.
Port files-card-body.{jsx -> tsx} in preparation for work on the sorting function. Create an enum type for our various sort options and use it in preference to hard-coded string values: that let's us drop the `default:` on our `switch()` over the possible sort types: TypeScript is now doing exhaustiveness checking for us. This found some bugs: - an invalid sort order value in localestore of the browser could cause an oops by returning an undefined sort function (due to the explicit return in default case of our `switch` in `compare()`. We now use an as_sort() function when reading the value to make sure it's one of our supported sort orders. - fsinfo says that the `.size` and `.mtime` fields might be missing, so we need to handle those cases, both in sorting and on display. `.name` is guaranteed to be present, since we set it ourselves - there were some very fine edge cases in our handling of selection: it's not clear if these could lead to errors in practice, but let's clean them up
If we tie on the mtime or the file size then fall back to sorting based on name — it's always unique. This should give us a fully-defined sort order for all directories. We can now remove a TODO: from a testcase.
779a7ae
to
71c5d01
Compare
|
||
// treat non-regular files and infos with missing 'size' field as having size of zero | ||
const size = (a: FolderFileInfo) => (a.type === "reg" && a.size) || 0; | ||
const mtime = (a: FolderFileInfo) => a.mtime || 0; // fallbak for missing .mtime field |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added line is not executed by any test.
case Sort.za: | ||
return (a, b) => dir_sort(a, b) || name_sort(b, a); | ||
case Sort.first_modified: | ||
return (a, b) => dir_sort(a, b) || (mtime(a) - mtime(b)) || name_sort(a, b); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added line is not executed by any test.
case Sort.first_modified: | ||
return (a, b) => dir_sort(a, b) || (mtime(a) - mtime(b)) || name_sort(a, b); | ||
case Sort.last_modified: | ||
return (a, b) => dir_sort(a, b) || (mtime(b) - mtime(a)) || name_sort(a, b); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added line is not executed by any test.
@@ -423,7 +447,7 @@ const Row = React.memo(function Item({ file, isSelected }) { | |||
dataLabel="date" | |||
modifier="nowrap" | |||
> | |||
{timeformat.dateTime(file.mtime * 1000)} | |||
{file.mtime ? timeformat.dateTime(file.mtime * 1000) : null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added line is not executed by any test.
"-R", | ||
...clipboard, | ||
currentPath | ||
]).catch(err => addAlert(err.message, AlertVariant.danger, `${new Date().getTime()}`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added line is not executed by any test.
{ | ||
id: "copy-item", | ||
title: _("Copy"), | ||
onClick: () => setClipboard(selected.map(s => path.join("/") + "/" + s.name)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This added line is not executed by any test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the commits could have been separate PRs, the menu changes will need another round of rebasing for feature PRs.
@@ -55,7 +56,7 @@ export const SettingsDropdown = ({ showHidden, setShowHidden }) => { | |||
return ( | |||
<Dropdown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self, this is basically the KebabDropdown
...
const _ = cockpit.gettext; | ||
|
||
type MenuItem = { type: "divider" } | { | ||
type?: never, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeScript introduced a new type never, which indicates the values that will never occur.
But uh, this will be set to a string at some point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No.
This is basically a way of discriminating the union: either we have items that set type
to the literal string "divider"
, or it's forbidden from appearing at all (never
).
/* | ||
* This file is part of Cockpit. | ||
* | ||
* Copyright (C) 2017 Red Hat, Inc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2017? Sounds like a long time ago :)
"-R", | ||
...clipboard, | ||
currentPath | ||
]).catch(err => addAlert(err.message, AlertVariant.danger, `${new Date().getTime()}`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At some point a huge function becomes unreadable in a list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can move it back out of you prefer. It could also be a separate function — there are some other cases similar to this.
Needs some small fixes, so I retracted my +1. |
I opened cockpit-project/cockpit#20635 for the arch failure — it's definitely not our issue, but it seems to be aggravated by the test case change. I'm trying it again. We can't really land this with a test that goes red 50% of the time, though... |
So this needs a naughty now, as the Arch test always fails? |
Ya, It's a minor abuse of naughties, but from the standpoint of cockpit-files, |
|
If we tie on the mtime or the file size then fall back to sorting based on name — it's always unique.
This should give us a fully-defined sort order for all directories.