-
Notifications
You must be signed in to change notification settings - Fork 29
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
Keep the path as string instead of an array #678
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ | |
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
import React, { useContext, useEffect, useMemo, useState } from "react"; | ||
import React, { useContext, useEffect, useState } from "react"; | ||
|
||
import { | ||
AlertGroup, Alert, AlertVariant, AlertActionCloseButton | ||
|
@@ -67,6 +67,25 @@ export const FilesContext = React.createContext({ | |
|
||
export const useFilesContext = () => useContext(FilesContext); | ||
|
||
export const usePath = () => { | ||
const { options } = usePageLocation(); | ||
let currentPath = decodeURIComponent(options.path?.toString() || "/"); | ||
|
||
// Trim all trailing slashes | ||
currentPath = currentPath.replace(/\/+$/, ''); | ||
|
||
// Our path will always be `/foo/` formatted | ||
if (!currentPath.endsWith("/")) { | ||
currentPath += "/"; | ||
} | ||
|
||
if (!currentPath.startsWith("/")) { | ||
currentPath = `/${currentPath}`; | ||
Comment on lines
+82
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These 2 added lines are not executed by any test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test this in a follow up! |
||
} | ||
|
||
return currentPath; | ||
}; | ||
|
||
export const Application = () => { | ||
const { options } = usePageLocation(); | ||
const [loading, setLoading] = useState(true); | ||
|
@@ -79,10 +98,7 @@ export const Application = () => { | |
const [alerts, setAlerts] = useState<Alert[]>([]); | ||
const [cwdInfo, setCwdInfo] = useState<FileInfo | null>(null); | ||
|
||
const currentPath = decodeURIComponent(options.path?.toString() || ""); | ||
// the function itself is not expensive, but `path` is later used in expensive computation | ||
// and changing its reference value on every render causes performance issues | ||
const path = useMemo(() => currentPath?.split("/"), [currentPath]); | ||
const path = usePath(); | ||
|
||
useEffect(() => { | ||
cockpit.user().then(user => { | ||
|
@@ -102,7 +118,7 @@ export const Application = () => { | |
setSelected([]); | ||
|
||
const client = new FsInfoClient( | ||
`/${currentPath}`, | ||
path, | ||
["type", "mode", "size", "mtime", "user", "group", "target", "entries", "targets"], | ||
{ superuser: 'try' } | ||
); | ||
|
@@ -126,7 +142,7 @@ export const Application = () => { | |
client.close(); | ||
}; | ||
}, | ||
[options, currentPath] | ||
[options, path] | ||
); | ||
|
||
if (loading) | ||
|
@@ -159,9 +175,7 @@ export const Application = () => { | |
</Alert> | ||
))} | ||
</AlertGroup> | ||
<FilesBreadcrumbs | ||
path={path} | ||
/> | ||
<FilesBreadcrumbs path={path} /> | ||
<PageSection> | ||
<Sidebar isPanelRight hasGutter> | ||
<SidebarContent> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,18 +37,20 @@ import { basename } from "./common"; | |
|
||
const _ = cockpit.gettext; | ||
|
||
function BookmarkButton({ path }: { path: string[] }) { | ||
function BookmarkButton({ path }: { path: string }) { | ||
const [isOpen, setIsOpen] = React.useState(false); | ||
const [user, setUser] = React.useState<cockpit.UserInfo | null>(null); | ||
const [bookmarks, setBookmarks] = React.useState<string[]>([]); | ||
const [bookmarkHandle, setBookmarkHandle] = React.useState<cockpit.FileHandle<string> | null>(null); | ||
|
||
const { addAlert, cwdInfo } = useFilesContext(); | ||
|
||
const currentPath = path.join("/") || "/"; | ||
const defaultBookmarks = []; | ||
if (user?.home) | ||
defaultBookmarks.push({ name: _("Home"), loc: user?.home }); // TODO: add trash | ||
if (user?.home) { | ||
// Ensure the home dir has a trailing / like the path | ||
const home_dir = user.home.endsWith('/') ? user.home : `${user.home}/`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, never seen There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This added line is not executed by any test. |
||
defaultBookmarks.push({ name: _("Home"), loc: home_dir }); // TODO: add trash | ||
} | ||
|
||
const parse_uri = (line: string) => { | ||
// Drop everything after the space, we don't show renames | ||
|
@@ -58,8 +60,15 @@ function BookmarkButton({ path }: { path: string[] }) { | |
line = line.replace('file://', ''); | ||
|
||
// Nautilus decodes urls as paths can contain spaces | ||
return line.split('/').map(part => decodeURIComponent(part)) | ||
let bookmark_path = line.split('/').map(part => decodeURIComponent(part)) | ||
.join('/'); | ||
|
||
// Ensure the bookmark has a trailing slash | ||
if (!bookmark_path.endsWith('/')) { | ||
bookmark_path = `${bookmark_path}/`; | ||
} | ||
|
||
return bookmark_path; | ||
}; | ||
|
||
useInit(async () => { | ||
|
@@ -99,11 +108,11 @@ function BookmarkButton({ path }: { path: string[] }) { | |
|
||
try { | ||
await bookmarkHandle.modify((old_content: string) => { | ||
if (bookmarks.includes(currentPath)) { | ||
return old_content.split('\n').filter(line => parse_uri(line) !== currentPath) | ||
if (bookmarks.includes(path)) { | ||
return old_content.split('\n').filter(line => parse_uri(line) !== path) | ||
.join('\n'); | ||
} else { | ||
const newBoomark = "file://" + path.map(part => encodeURIComponent(part)) | ||
const newBoomark = "file://" + path.split('/').map(part => encodeURIComponent(part)) | ||
.join('/') + "\n"; | ||
return (old_content || '') + newBoomark; | ||
} | ||
|
@@ -129,8 +138,8 @@ function BookmarkButton({ path }: { path: string[] }) { | |
return null; | ||
|
||
let actionText = null; | ||
if (currentPath !== user.home) { | ||
if (bookmarks.includes(currentPath)) { | ||
if (!defaultBookmarks.some(bkmark => bkmark.loc === path)) { | ||
if (bookmarks.includes(path)) { | ||
actionText = _("Remove current directory"); | ||
} else if (cwdInfo !== null) { | ||
actionText = _("Add bookmark"); | ||
|
@@ -182,16 +191,11 @@ function BookmarkButton({ path }: { path: string[] }) { | |
); | ||
} | ||
|
||
const PathBreadcrumbs = ({ path }: { path: string[] }) => { | ||
// HACK: strip extraneous slashes as PF's breadcrumb can't handle them | ||
// Refactor the path to be the fullpath not an array of strings | ||
if (path.length >= 2 && path[0] === '' && path[1] === '') { | ||
path = path.slice(1); | ||
} | ||
|
||
if (path.length > 1 && path[path.length - 1] === '') { | ||
path = path.slice(path.length - 1); | ||
} | ||
const PathBreadcrumbs = ({ path }: { path: string }) => { | ||
// Strip the trailing slash as it gets in the way when splitting paths and | ||
// adds an extra trailing slash in the UI which we don't want to show and | ||
// causes duplicate keys for the path `/`. | ||
const path_array = path.replace(/\/$/, "").split("/"); | ||
|
||
function navigate(event: React.MouseEvent<HTMLElement>) { | ||
const { button, ctrlKey, metaKey } = event; | ||
|
@@ -213,8 +217,8 @@ const PathBreadcrumbs = ({ path }: { path: string[] }) => { | |
|
||
return ( | ||
<Breadcrumb onClick={(event) => navigate(event)}> | ||
{path.map((dir, i) => { | ||
const url_path = path.slice(0, i + 1).join("/") || '/'; | ||
{path_array.map((dir, i) => { | ||
const url_path = path_array.slice(0, i + 1).join("/") || '/'; | ||
// We can't use a relative path as that will use the iframe's | ||
// url while we want the outer shell url. And we can't obtain | ||
// the full path of the shell easily, so a middle click will | ||
|
@@ -226,7 +230,7 @@ const PathBreadcrumbs = ({ path }: { path: string[] }) => { | |
key={url_path} | ||
data-location={url_path} | ||
to={link} | ||
isActive={i === path.length - 1} | ||
isActive={i === path_array.length - 1} | ||
> | ||
{i === 0 && | ||
<Tooltip | ||
|
@@ -244,7 +248,7 @@ const PathBreadcrumbs = ({ path }: { path: string[] }) => { | |
}; | ||
|
||
// eslint-disable-next-line max-len | ||
export function FilesBreadcrumbs({ path }: { path: string[] }) { | ||
export function FilesBreadcrumbs({ path }: { path: string }) { | ||
const [editMode, setEditMode] = React.useState(false); | ||
const [newPath, setNewPath] = React.useState<string | null>(null); | ||
|
||
|
@@ -265,7 +269,7 @@ export function FilesBreadcrumbs({ path }: { path: string[] }) { | |
|
||
const enableEditMode = () => { | ||
setEditMode(true); | ||
setNewPath(path.join("/") || "/"); | ||
setNewPath(path); | ||
}; | ||
|
||
const changePath = () => { | ||
|
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 test :)