Download File Option (#80)

This adds to the context menu to give the ability to download a file. It
also fixes a couple bugs and improves some formatting of the directory
view.
This commit is contained in:
Sylvie Crowe 2024-06-26 12:14:59 -07:00 committed by GitHub
parent 566f6764c2
commit 0a8c97858c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 42 additions and 6 deletions

View File

@ -1,6 +1,7 @@
// Copyright 2024, Command Line Inc. // Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import { getBackendHostPort } from "@/app/store/global";
import * as keyutil from "@/util/keyutil"; import * as keyutil from "@/util/keyutil";
import * as electron from "electron"; import * as electron from "electron";
import fs from "fs"; import fs from "fs";
@ -421,6 +422,13 @@ electron.ipcMain.on("getPlatform", (event) => {
event.returnValue = unamePlatform; event.returnValue = unamePlatform;
}); });
electron.ipcMain.on("download", (event, payload) => {
const window = electron.BrowserWindow.fromWebContents(event.sender);
const baseName = payload.filePath.split(/[\\/]/).pop();
const streamingUrl = getBackendHostPort() + "/wave/stream-file?path=" + encodeURIComponent(payload.filePath);
window.webContents.downloadURL(streamingUrl);
});
electron.ipcMain.on("getCursorPoint", (event) => { electron.ipcMain.on("getCursorPoint", (event) => {
const window = electron.BrowserWindow.fromWebContents(event.sender); const window = electron.BrowserWindow.fromWebContents(event.sender);
const screenPoint = electron.screen.getCursorScreenPoint(); const screenPoint = electron.screen.getCursorScreenPoint();

View File

@ -11,4 +11,5 @@ contextBridge.exposeInMainWorld("api", {
openNewWindow: () => ipcRenderer.send("openNewWindow"), openNewWindow: () => ipcRenderer.send("openNewWindow"),
showContextMenu: (menu, position) => ipcRenderer.send("contextmenu-show", menu, position), showContextMenu: (menu, position) => ipcRenderer.send("contextmenu-show", menu, position),
onContextMenuClick: (callback) => ipcRenderer.on("contextmenu-click", callback), onContextMenuClick: (callback) => ipcRenderer.on("contextmenu-click", callback),
downloadFile: (filePath) => ipcRenderer.send("download", { filePath }),
}); });

View File

@ -15,7 +15,7 @@ import clsx from "clsx";
import * as jotai from "jotai"; import * as jotai from "jotai";
import React from "react"; import React from "react";
import { ContextMenuModel } from "../store/contextmenu"; import { ContextMenuModel } from "../store/contextmenu";
import { atoms, createBlock } from "../store/global"; import { atoms, createBlock, getApi } from "../store/global";
import "./directorypreview.less"; import "./directorypreview.less";
@ -52,6 +52,7 @@ function getBestUnit(bytes: number, si: boolean = false, sigfig: number = 3): st
while (currentValue > divisor && idx < units.length - 1) { while (currentValue > divisor && idx < units.length - 1) {
currentUnit = units[idx]; currentUnit = units[idx];
currentValue /= divisor; currentValue /= divisor;
idx += 1;
} }
return `${parseFloat(currentValue.toPrecision(sigfig))}${displaySuffixes[currentUnit]}`; return `${parseFloat(currentValue.toPrecision(sigfig))}${displaySuffixes[currentUnit]}`;
@ -136,9 +137,20 @@ function handleFileContextMenu(e: React.MouseEvent<HTMLDivElement>, path: string
await services.FileService.DeleteFile(path).catch((e) => console.log(e)); //todo these errors need a popup await services.FileService.DeleteFile(path).catch((e) => console.log(e)); //todo these errors need a popup
}, },
}); });
menu.push({
label: "Download File",
click: async () => {
getApi().downloadFile(path);
},
});
ContextMenuModel.showContextMenu(menu, e); ContextMenuModel.showContextMenu(menu, e);
} }
function cleanMimetype(input: string): string {
const truncated = input.split(";")[0];
return truncated.trim();
}
function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) { function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) {
let settings = jotai.useAtomValue(atoms.settingsConfigAtom); let settings = jotai.useAtomValue(atoms.settingsConfigAtom);
const getIconFromMimeType = React.useCallback( const getIconFromMimeType = React.useCallback(
@ -154,11 +166,23 @@ function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) {
}, },
[settings.mimetypes] [settings.mimetypes]
); );
const getIconColor = React.useCallback(
(mimeType: string): string => {
let iconColor = settings.mimetypes[mimeType]?.color ?? "inherit";
return iconColor;
},
[settings.mimetypes]
);
const columns = React.useMemo( const columns = React.useMemo(
() => [ () => [
columnHelper.accessor("mimetype", { columnHelper.accessor("mimetype", {
cell: (info) => <i className={getIconFromMimeType(info.getValue() ?? "")}></i>, cell: (info) => (
header: () => <span>Type</span>, <i
className={getIconFromMimeType(info.getValue() ?? "")}
style={{ color: getIconColor(info.getValue() ?? "") }}
></i>
),
header: () => <span></span>,
id: "logo", id: "logo",
size: 25, size: 25,
enableSorting: false, enableSorting: false,
@ -191,7 +215,7 @@ function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) {
sortingFn: "auto", sortingFn: "auto",
}), }),
columnHelper.accessor("mimetype", { columnHelper.accessor("mimetype", {
cell: (info) => <span className="dir-table-type">{info.getValue()}</span>, cell: (info) => <span className="dir-table-type">{cleanMimetype(info.getValue() ?? "")}</span>,
header: () => <span>Type</span>, header: () => <span>Type</span>,
sortingFn: "alphanumeric", sortingFn: "alphanumeric",
}), }),

View File

@ -36,6 +36,7 @@ declare global {
onContextMenuClick: (callback: (id: string) => void) => void; onContextMenuClick: (callback: (id: string) => void) => void;
onNavigate: (callback: (url: string) => void) => void; onNavigate: (callback: (url: string) => void) => void;
onIframeNavigate: (callback: (url: string) => void) => void; onIframeNavigate: (callback: (url: string) => void) => void;
downloadFile: (path: string) => void;
}; };
type ElectronContextMenuItem = { type ElectronContextMenuItem = {

View File

@ -180,6 +180,7 @@ declare global {
// wconfig.MimeTypeConfigType // wconfig.MimeTypeConfigType
type MimeTypeConfigType = { type MimeTypeConfigType = {
icon: string; icon: string;
color: string;
}; };
// waveobj.ORef // waveobj.ORef

View File

@ -39,7 +39,8 @@ type DateTimeFormatConfigType struct {
} }
type MimeTypeConfigType struct { type MimeTypeConfigType struct {
Icon string `json:"icon"` Icon string `json:"icon"`
Color string `json:"color"`
} }
type BlockHeaderOpts struct { type BlockHeaderOpts struct {
@ -67,7 +68,7 @@ func getSettingsConfigDefaults() SettingsConfigType {
"audio": {Icon: "file-audio"}, "audio": {Icon: "file-audio"},
"application/pdf": {Icon: "file-pdf"}, "application/pdf": {Icon: "file-pdf"},
"application/json": {Icon: "file-lines"}, "application/json": {Icon: "file-lines"},
"directory": {Icon: "folder"}, "directory": {Icon: "folder", Color: "#2e62d2"},
"font": {Icon: "book-font"}, "font": {Icon: "book-font"},
"image": {Icon: "file-image"}, "image": {Icon: "file-image"},
"text": {Icon: "file-lines"}, "text": {Icon: "file-lines"},