-
Notifications
You must be signed in to change notification settings - Fork 38
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
Implement bookmark support #542
Implement bookmark support #542
Conversation
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.
First thought: https://www.freedesktop.org/wiki/Specifications/desktop-bookmark-spec/
Let's not make this per-browser?
I take it back. Nothing seems to use this. Nautilus stores this in ~/.config/gtk-3.0/bookmarks in a flatfile, one per line. I think it makes sense to match GNOME behaviour where possible, and despite the odd filename, I think it's appropriate in this case too. |
You mean, Cockpit would write to |
This ain't working, lets not make this dependent on a fixed offset. |
This is really not something we should do, it is relevant for the desktop but Cockpit isn't the desktop we store all our settings in localstorage so this would deviate from it (for no real reason) |
0b4c263
to
3302a17
Compare
Thinking about this again, I don't like this solution at all, it is super random and a GNOME implementation detail. The format is not a simple "flat file" at all it is formatted as:
|
32b78e2
to
4a77ff0
Compare
src/files-breadcrumbs.tsx
Outdated
if (old_content === null) { | ||
return `${currentPath}\n`; | ||
} else { | ||
return old_content + `${currentPath}\n`; |
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 should be tested, is this a coverage issue or a missing test?
src/files-breadcrumbs.tsx
Outdated
} catch (err) { | ||
console.error("Unable to save bookmark", err); |
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.
We could fire an addAlert
here but testing this might be tricky but we could slap a chattr +i
on bookmarks
Nautilus allows renaming bookmarks. We don't parse the file correctly to handle that: It tries to use the renamed version as part of the path, and we get a broken page like this:
I do see that a remote path (ssh) does not show up, which is expected. Additionally, there are a few problems related to the implementation:
And, for a separate PR:
|
Additionally, when you have a space in a path (which is encoded in the URI as Do this:
|
So renaming bookmarks means not changing the entry but giving it a different name
That is rather expensive and we have no UI for it. What do we do, hide them? Mark them invalid.
Right, this is patternfly/patternfly#6632
This is a PF design, it is intentionally done in their dropdown demo. Nothing I can do here.
That is a bug, I'll look into it!
It has been broken by the re-design/layout, needs fixing. #568 Meanwhile there is a PR to add pixel tests to catch these issues. |
Yes, but we can just drop the space and everything after, only caring about the path. We don't have to show the custom name; we just can't break the path due to the custom name.
Well, at least it seems you can still browse to the directory (with a page with a broken layout) and remove the bookmark. I guess that's OK for this PR.
It's literally broken — but, yes, they need to fix it. Issue @ cockpit-project/cockpit#20526 A side effect is that there are two items that have highlights, not just the first one having it by default: It's outside the scope of this PR, agreed. |
e117ace
to
16a53d6
Compare
I had to work around widgets being inside the breadcrumb bar. I don't think that's supposed to happen. I think it's supposed to be a top bar with layout that has buttons, then the breadcrumbs, and the rest. I think we have something like this:
But it's supposed to be more like this:
|
src/app.scss
Outdated
@@ -78,6 +78,11 @@ | |||
|
|||
button.pf-m-secondary.breadcrumb-edit-button { | |||
margin-inline-end: var(--pf-v5-global--spacer--sm); | |||
|
|||
// PatternFly doesn't expect icons in buttons in breadcrumbs, so we need to adjust for that |
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 comment seems wrong? This is the edit path button.
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.
The edit path button is a button with an icon and it's nested under the breadcrumb component... that's exactly what this comment is talking about.
I'm pretty sure the breadcrumb component is supposed to contain breadcrumbs and nothing else. See my comment above @ #542 (comment)
I added some workaround CSS meanwhile, which is what this comment is talking about.
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.
(The thing is, it also affects the bookmark dropdown, which also is in the breadcrumb widget, which also should probably not be. And to fix one, I needed to fix both and refactor the CSS a bit, which is why it's in this PR.)
3093844
to
53085a7
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.
I mostly just reviewed the "inside" bits, and they look good (but see optional suggestions). I don't feel able to sign off on the UI.
src/files-breadcrumbs.tsx
Outdated
|
||
try { | ||
await bookmarkHandle.modify((old_content: string) => { | ||
const newBoomark = `${encodeURI(currentPath)}\n`; |
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 see what you do now. This seems strange. We add the file:///
in one place and then carry a weird quasi-URI around for a while and then add the escaping once we get here. Why not just do everything in one spot? I also still think it would make sense to do the encoding on the path components (with encodeURIComponent
) and then join them together and add file:///
.
try { | ||
await bookmarkHandle.modify((old_content: string) => { | ||
const newBoomark = `${encodeURI(currentPath)}\n`; | ||
if (bookmarks.includes(currentPath)) { |
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 think this will fail to correctly detect a bookmark which has escaped characters in it, since this is still the unescaped version. Probably in that case we'll add the bookmark twice?
b87c7cb
to
2f4c640
Compare
src/files-breadcrumbs.tsx
Outdated
<Divider key="bookmark-divider" />} | ||
{bookmarks.map((bookmark: string) => ( | ||
<DropdownItem key={bookmark} value={bookmark}> | ||
{bookmark.replace("file://", "")} |
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 longer needed now.
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 looks substantially improved from previous versions, thanks.
A couple comments below.
src/files-breadcrumbs.tsx
Outdated
const newBoomark = path.map(part => encodeURIComponent(part)) | ||
.join('/'); | ||
if (bookmarks.includes(currentPath)) { | ||
return old_content.split('\n').filter(line => !line.startsWith(`file://${newBoomark}`)) |
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 will fail to detect an entry in the bookmarks file which had a different encoding than the one we'd have selected. We'll match our bookmarks.includes()
check above (because we parsed the entries out for that one) but fail to match on our encoding.
I think it might be interesting to reuse the parser code from our .watch()
handler, which is quite good. It could find the line that matches, and then we could remove that one from the list. We know we're sure to find it, because it was the line that resulted in us having the bookmark in the first place.
That could work as a filter as well, something like
return old_content.split('\n').filter(line => parse_uri(line) !== currentPath).join('\n')
This is probably worth adding a test case over.
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.
Also: this code is slightly weird. It considers the zero bytes after the last newline in the file as if they were a bookmark entry. In that case, it's zero bytes, so it doesn't match, but ... it feels weird. It might be nice to have some "proper" line-splitting code here.
I wonder if you'd consider using a simple "syntax" object on your file handle to take care of the line splitting and re-joining. Then you could just interact directly with lines in both the watcher and the modifier.
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 think it might be interesting to reuse the parser code from our .watch() handler, which is quite good. It could find the line that matches, and then we could remove that one from the list. We know we're sure to find it, because it was the line that resulted in us having the bookmark in the first place.
Took me a while to parse this, the parse_uri
approach seems to be fine. I'm happy to make those changes.
Also: this code is slightly weird. It considers the zero bytes after the last newline in the file as if they were a bookmark entry. In that case, it's zero bytes, so it doesn't match, but ... it feels weird. It might be nice to have some "proper" line-splitting code here.
The zero byte case, I mean, in reality it is hardly a problem? I have no idea what you mean with "proper" line splitting, just trimming out the junk?
I wonder if you'd consider using a simple "syntax" object on your file handle to take care of the line splitting and re-joining. Then you could just interact directly with lines in both the watcher and the modifier.
Like how would that help or make things easier? Its still going to be JavaScript which does the parsing and it would mean we have to re-construct the whole file when modifying from the state we have, which is imo not what we want.
We don't even care about a zero byte in /etc/passwd, why is this file so special? What we do do is trim whitespace (newline or whitespace character). Which we can also implement.
content = (content || "").trim();
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.
proper line splitting
Like something that converts "a\nb\n"
to ['a', 'b']
rather than ['a', 'b', '']
and then ['a', 'b']
to "a\nb\n" rather than
"a\nb"`.
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.
The "bookmark file handling" code now looks completely correct to me. Thanks for iterating on this. See an idea for a test case, though.
UI/React/PF review needs to come from someone else.
src/files-breadcrumbs.tsx
Outdated
@@ -122,7 +125,7 @@ function BookmarkButton({ path }: { path: string[] }) { | |||
const newBoomark = path.map(part => encodeURIComponent(part)) |
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 could probably be moved inside of the } else {
case now that you only use the variable on that path. You might also add file://
to it as part of the same process, instead of doing it in two steps.
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 is still unaddressed. If you do another round, please move this closer to where it's used.
src/files-breadcrumbs.tsx
Outdated
@@ -122,7 +125,7 @@ function BookmarkButton({ path }: { path: string[] }) { | |||
const newBoomark = path.map(part => encodeURIComponent(part)) | |||
.join('/'); | |||
if (bookmarks.includes(currentPath)) { | |||
return old_content.split('\n').filter(line => !line.startsWith(`file://${newBoomark}`)) |
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 looks like it'll work now.
A sketch for a testcase: take a "special" character that we know won't be escaped, something like -
(which can also be written %2d
).
Then
mkdir /home/admin/test-dir
echo file:///home/admin/test%2ddir >> [bookmarks]
Then verify that:
- we can use the bookmark to navigate to the new directory (even though it has a weird encoding)
- we can remove the bookmark
- we can check the file to see that the bookmark has been removed, and we can check the UI to see that it's been updated as expected
- we can add it back again
- the encoding that it got added back with is different than the original one (ie: check that
test-dir
appears in the bookmarks file now)
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.
Right because:
decodeURIComponent("test%2ddir")
"test-dir"
encodeURIComponent("test-dir")
"test-dir"
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 just reworked the test with spaces to use %2d so this is now fully tested.
Bugs:
Also:
|
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.
From me: this is ready to go. It seemed @garrett had some comments on the last round so I'll let him chime in on this one with the official +1/-1.
src/files-breadcrumbs.tsx
Outdated
@@ -122,7 +125,7 @@ function BookmarkButton({ path }: { path: string[] }) { | |||
const newBoomark = path.map(part => encodeURIComponent(part)) |
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 is still unaddressed. If you do another round, please move this closer to where it's used.
The removal is already fixed and the other case I can't reproduce. |
9eac268
to
80e9d1a
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.
Approving again; my last approval from a minute or two ago with the screenshot and mentioning the follow-ups still stands. 😉
Allow users to bookmark a current directory (saved in the browsers localstorage) to which they can easily change back via a dropdown. By default the users home directory is added as bookmark. Closes: cockpit-project#522
return ( | ||
<Dropdown | ||
isOpen={isOpen} | ||
onOpenChange={(isOpen: boolean) => setIsOpen(isOpen)} |
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.
🚀 los geht's
return old_content.split('\n').filter(line => parse_uri(line) !== currentPath) | ||
.join('\n'); | ||
} else { | ||
const newBoomark = "file://" + path.map(part => encodeURIComponent(part)) |
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.
much better, thanks!
Closes: #522
Bookmark support
Users can now bookmark the current directory and easily navigate to previously bookmarked directories.