Skip to content
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

Directly use fsinfo state #434

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,8 @@ interface Alert {
variant: AlertVariant,
}

export interface FolderFileInfo extends FileInfo {
name: string,
to: string | null,
export interface FolderInfoMap {
[key: string]: FileInfo | null
}

interface FilesContextType {
Expand All @@ -65,8 +64,8 @@ export const Application = () => {
const [loading, setLoading] = useState(true);
const [loadingFiles, setLoadingFiles] = useState(true);
const [errorMessage, setErrorMessage] = useState("");
const [files, setFiles] = useState<FolderFileInfo[]>([]);
const [selected, setSelected] = useState<FolderFileInfo[]>([]);
const [files, setFiles] = useState<FolderInfoMap>({});
const [selected, setSelected] = useState<string[]>([]);
const [showHidden, setShowHidden] = useState(localStorage.getItem("files:showHiddenFiles") === "true");
const [clipboard, setClipboard] = useState<string[]>([]);
const [alerts, setAlerts] = useState<Alert[]>([]);
Expand Down Expand Up @@ -103,13 +102,8 @@ export const Application = () => {
setLoadingFiles(!(state.info || state.error));
setCwdInfo(state.info);
setErrorMessage(state.error?.message ?? "");
const entries = Object.entries(state?.info?.entries || {});
const files = entries.map(([name, attrs]) => ({
...attrs,
name,
to: info.target(name)?.type ?? null
}));
setFiles(files);
// to: info.target(name)?.type ?? null
setFiles(state?.info?.entries || {});
});
},
[options, currentPath]
Expand Down Expand Up @@ -170,8 +164,7 @@ export const Application = () => {
<SidebarPanel className="sidebar-panel" width={{ default: "width_25" }}>
<SidebarPanelDetails
path={path}
selected={selected.map(s => files.find(f => f.name === s.name))
.filter(s => s !== undefined)}
selected={selected}
showHidden={showHidden} setSelected={setSelected}
clipboard={clipboard} setClipboard={setClipboard}
files={files}
Expand Down
109 changes: 62 additions & 47 deletions src/fileActions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,19 @@ import { useFilesContext } from "./app";

const _ = cockpit.gettext;

export const editPermissions = (Dialogs, selected, path) => {
export const editPermissions = (Dialogs, files, selected, path) => {
Dialogs.show(
<EditPermissionsModal
selected={selected} path={path}
files={files}
selected={selected}
path={path}
/>
);
};

export const ConfirmDeletionDialog = ({
path,
files,
selected,
setSelected,
}) => {
Expand All @@ -66,28 +69,28 @@ export const ConfirmDeletionDialog = ({
if (selected.length > 1) {
modalTitle = cockpit.format(forceDelete ? _("Force delete $0 items") : _("Delete $0 items?"), selected.length);
} else {
const selectedItem = selected[0];
if (selectedItem.type === "reg") {
const fileInfo = files[selected[0]];
if (fileInfo?.type === "reg") {
modalTitle = cockpit.format(
forceDelete ? _("Force delete file $0?") : _("Delete file $0?"), selectedItem.name
forceDelete ? _("Force delete file $0?") : _("Delete file $0?"), selected[0]
);
} else if (selectedItem.type === "lnk") {
} else if (fileInfo?.type === "lnk") {
modalTitle = cockpit.format(
forceDelete ? _("Force delete link $0?") : _("Delete link $0?"), selectedItem.name
forceDelete ? _("Force delete link $0?") : _("Delete link $0?"), selected[0]
Copy link
Contributor

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.

);
} else if (selectedItem.type === "dir") {
} else if (fileInfo?.type === "dir") {
modalTitle = cockpit.format(
forceDelete ? _("Force delete directory $0?") : _("Delete directory $0?"), selectedItem.name
forceDelete ? _("Force delete directory $0?") : _("Delete directory $0?"), selected[0]
Copy link
Contributor

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.

);
} else {
modalTitle = cockpit.format(forceDelete ? _("Force delete $0") : _("Delete $0?"), selectedItem.name);
modalTitle = cockpit.format(forceDelete ? _("Force delete $0") : _("Delete $0?"), selected[0]);
Copy link
Contributor

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.

}
}

const deleteItem = () => {
const args = ["rm", "-r"];
// TODO: Make force more sensible https://github.com/cockpit-project/cockpit-files/issues/363
cockpit.spawn([...args, ...selected.map(f => path + f.name)], { err: "message", superuser: "try" })
cockpit.spawn([...args, ...selected.map(f => path + f)], { err: "message", superuser: "try" })
.then(() => {
setSelected([]);
Dialogs.close();
Expand Down Expand Up @@ -183,27 +186,29 @@ const CreateDirectoryModal = ({ currentPath }) => {
);
};

const RenameItemModal = ({ path, selected }) => {
const RenameItemModal = ({ path, files, selected }) => {
const Dialogs = useDialogs();
const [name, setName] = useState(selected.name);
const [name, setName] = useState(selected);
const [nameError, setNameError] = useState(null);
const [errorMessage, setErrorMessage] = useState(undefined);

const fileInfo = files[selected];

let title;
if (selected.type === "reg") {
title = cockpit.format(_("Rename file $0"), selected.name);
} else if (selected.type === "lnk") {
title = cockpit.format(_("Rename link $0"), selected.name);
} else if (selected.type === "dir") {
title = cockpit.format(_("Rename directory $0"), selected.name);
if (fileInfo?.type === "reg") {
title = cockpit.format(_("Rename file $0"), selected);
} else if (fileInfo?.type === "lnk") {
title = cockpit.format(_("Rename link $0"), selected);
} else if (fileInfo?.type === "dir") {
Comment on lines +200 to +202
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 3 added lines are not executed by any test.

title = cockpit.format(_("Rename directory $0"), selected);
} else {
title = _("Rename $0", selected.name);
title = _("Rename $0", selected);
Copy link
Contributor

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.

}

const renameItem = () => {
const newPath = path.join("/") + "/" + name;

cockpit.spawn(["mv", path.join("/") + "/" + selected.name, newPath],
cockpit.spawn(["mv", path.join("/") + "/" + selected, newPath],
{ superuser: "try", err: "message" })
.then(() => {
Dialogs.close();
Expand Down Expand Up @@ -252,19 +257,23 @@ const RenameItemModal = ({ path, selected }) => {
);
};

const EditPermissionsModal = ({ selected, path }) => {
const EditPermissionsModal = ({ files, selected, path }) => {
const Dialogs = useDialogs();
const { cwdInfo } = useFilesContext();

let fileInfo = files[selected[0]];
let filename = selected[0];
// Nothing selected means we act on the current working directory
if (!selected) {
if (selected.length === 0) {
const directory_name = path[path.length - 1];
selected = { ...cwdInfo, isCwd: true, name: directory_name };
fileInfo = { ...cwdInfo, isCwd: true };
filename = directory_name;
}
cockpit.assert(fileInfo, `cannot obtain file information for ${filename}`);

const [owner, setOwner] = useState(selected.user);
const [mode, setMode] = useState(selected.mode);
const [group, setGroup] = useState(selected.group);
const [owner, setOwner] = useState(fileInfo.user);
const [mode, setMode] = useState(fileInfo.mode);
const [group, setGroup] = useState(fileInfo.group);
const [errorMessage, setErrorMessage] = useState(undefined);
const accounts = useFile("/etc/passwd", { syntax: etcPasswdSyntax });
const groups = useFile("/etc/group", { syntax: etcGroupSyntax });
Expand All @@ -279,11 +288,11 @@ const EditPermissionsModal = ({ selected, path }) => {
};

const spawnEditPermissions = async () => {
const permissionChanged = mode !== selected.mode;
const ownerChanged = owner !== selected.user || group !== selected.group;
const permissionChanged = mode !== fileInfo.mode;
const ownerChanged = owner !== fileInfo.user || group !== fileInfo.group;

try {
const directory = selected?.isCwd ? path.join("/") : path.join("/") + "/" + selected.name;
const directory = fileInfo?.isCwd ? path.join("/") : path.join("/") + "/" + filename;
if (permissionChanged)
await cockpit.spawn(["chmod", mode.toString(8), directory],
{ superuser: "try", err: "message" });
Expand Down Expand Up @@ -319,8 +328,8 @@ const EditPermissionsModal = ({ selected, path }) => {
position="top"
variant={ModalVariant.small}
/* Translators: $0 represents a filename */
title={cockpit.format(_("“$0” permissions"), selected.name)}
description={inode_types[selected.type] || "Unknown type"}
title={cockpit.format(_("“$0” permissions"), filename)}
description={inode_types[fileInfo.type] || "Unknown type"}
Copy link
Contributor

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.

isOpen
onClose={Dialogs.close}
footer={
Expand Down Expand Up @@ -435,14 +444,14 @@ const setDirectoryName = (val, setName, setNameError, setErrorMessage) => {
}
};

const downloadFile = (currentPath, selected) => {
const downloadFile = (currentPath, filename) => {
const query = window.btoa(JSON.stringify({
payload: "fsread1",
binary: "raw",
path: `${currentPath}/${selected.name}`,
path: `${currentPath}/${filename}`,
superuser: "try",
external: {
"content-disposition": `attachment; filename="${selected.name}"`,
"content-disposition": `attachment; filename="${filename}"`,
"content-type": "application/octet-stream",
}
}));
Expand All @@ -451,7 +460,7 @@ const downloadFile = (currentPath, selected) => {
window.open(`${prefix}?${query}`);
};

export const fileActions = (path, selected, setSelected, clipboard, setClipboard, addAlert, Dialogs) => {
export const fileActions = (files, path, selected, setSelected, clipboard, setClipboard, addAlert, Dialogs) => {
const currentPath = path.join("/") + "/";
const menuItems = [];

Expand All @@ -464,7 +473,7 @@ export const fileActions = (path, selected, setSelected, clipboard, setClipboard
]).catch(err => addAlert(err.message, "danger", new Date().getTime()));
};

if (selected.length === 0 || selected[0].name === path[path.length - 1]) {
if (selected.length === 0 || selected[0] === path[path.length - 1]) {
menuItems.push(
{
id: "paste-item",
Expand All @@ -482,22 +491,23 @@ export const fileActions = (path, selected, setSelected, clipboard, setClipboard
{
id: "edit-permissions",
title: _("Edit permissions"),
onClick: () => editPermissions(Dialogs, selected[0], path)
onClick: () => editPermissions(Dialogs, files, selected, path)
}
);
} else if (selected.length === 1) {
const fileInfo = files[selected[0]];
menuItems.push(
{
id: "copy-item",
title: _("Copy"),
onClick: () => setClipboard([currentPath + selected[0].name]),
onClick: () => setClipboard([currentPath + selected[0]]),
Copy link
Contributor

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.

},
...(selected[0].type === "dir")
...(fileInfo?.type === "dir")
? [
{
id: "paste-into-directory",
title: _("Paste into directory"),
onClick: () => spawnPaste(clipboard, [currentPath + selected[0].name]),
onClick: () => spawnPaste(clipboard, [currentPath + selected[0]]),
Copy link
Contributor

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.

isDisabled: clipboard.length === 0
}
]
Expand All @@ -506,16 +516,17 @@ export const fileActions = (path, selected, setSelected, clipboard, setClipboard
{
id: "edit-permissions",
title: _("Edit permissions"),
onClick: () => editPermissions(Dialogs, selected[0], path)
onClick: () => editPermissions(Dialogs, files, selected, path)
},
{
id: "rename-item",
title: _("Rename"),
onClick: () => {
Dialogs.show(
<RenameItemModal
files={files}
path={path}
selected={selected[0]}
selected={selected}
/>
);
},
Expand All @@ -528,14 +539,16 @@ export const fileActions = (path, selected, setSelected, clipboard, setClipboard
onClick: () => {
Dialogs.show(
<ConfirmDeletionDialog
selected={selected} path={currentPath}
files={files}
selected={selected}
path={currentPath}
setSelected={setSelected}
/>
);
}
},
);
if (selected[0].type === "reg")
if (fileInfo?.type === "reg")
menuItems.push(
{ type: "divider" },
{
Expand All @@ -549,7 +562,7 @@ export const fileActions = (path, selected, setSelected, clipboard, setClipboard
{
id: "copy-item",
title: _("Copy"),
onClick: () => setClipboard(selected.map(s => path.join("/") + "/" + s.name)),
onClick: () => setClipboard(selected.map(s => path.join("/") + "/" + s)),
Copy link
Contributor

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: "delete-item",
Expand All @@ -558,7 +571,9 @@ export const fileActions = (path, selected, setSelected, clipboard, setClipboard
onClick: () => {
Dialogs.show(
<ConfirmDeletionDialog
selected={selected} path={currentPath}
path={currentPath}
files={files}
selected={selected}
setSelected={setSelected}
/>
);
Expand Down
Loading