From 0a8c97858cbfc7d2be7fa1b8bca8520d1e491dec Mon Sep 17 00:00:00 2001 From: Sylvie Crowe <107814465+oneirocosm@users.noreply.github.com> Date: Wed, 26 Jun 2024 12:14:59 -0700 Subject: [PATCH] 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. --- emain/emain.ts | 8 +++++++ emain/preload.ts | 1 + frontend/app/view/directorypreview.tsx | 32 ++++++++++++++++++++++---- frontend/types/custom.d.ts | 1 + frontend/types/gotypes.d.ts | 1 + pkg/wconfig/settingsconfig.go | 5 ++-- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/emain/emain.ts b/emain/emain.ts index b0b315979..1c6a1411b 100644 --- a/emain/emain.ts +++ b/emain/emain.ts @@ -1,6 +1,7 @@ // Copyright 2024, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 +import { getBackendHostPort } from "@/app/store/global"; import * as keyutil from "@/util/keyutil"; import * as electron from "electron"; import fs from "fs"; @@ -421,6 +422,13 @@ electron.ipcMain.on("getPlatform", (event) => { 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) => { const window = electron.BrowserWindow.fromWebContents(event.sender); const screenPoint = electron.screen.getCursorScreenPoint(); diff --git a/emain/preload.ts b/emain/preload.ts index 3f91a2df1..b1350bf90 100644 --- a/emain/preload.ts +++ b/emain/preload.ts @@ -11,4 +11,5 @@ contextBridge.exposeInMainWorld("api", { openNewWindow: () => ipcRenderer.send("openNewWindow"), showContextMenu: (menu, position) => ipcRenderer.send("contextmenu-show", menu, position), onContextMenuClick: (callback) => ipcRenderer.on("contextmenu-click", callback), + downloadFile: (filePath) => ipcRenderer.send("download", { filePath }), }); diff --git a/frontend/app/view/directorypreview.tsx b/frontend/app/view/directorypreview.tsx index 9e3115959..90c0d0b58 100644 --- a/frontend/app/view/directorypreview.tsx +++ b/frontend/app/view/directorypreview.tsx @@ -15,7 +15,7 @@ import clsx from "clsx"; import * as jotai from "jotai"; import React from "react"; import { ContextMenuModel } from "../store/contextmenu"; -import { atoms, createBlock } from "../store/global"; +import { atoms, createBlock, getApi } from "../store/global"; 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) { currentUnit = units[idx]; currentValue /= divisor; + idx += 1; } return `${parseFloat(currentValue.toPrecision(sigfig))}${displaySuffixes[currentUnit]}`; @@ -136,9 +137,20 @@ function handleFileContextMenu(e: React.MouseEvent, path: string 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); } +function cleanMimetype(input: string): string { + const truncated = input.split(";")[0]; + return truncated.trim(); +} + function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) { let settings = jotai.useAtomValue(atoms.settingsConfigAtom); const getIconFromMimeType = React.useCallback( @@ -154,11 +166,23 @@ function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) { }, [settings.mimetypes] ); + const getIconColor = React.useCallback( + (mimeType: string): string => { + let iconColor = settings.mimetypes[mimeType]?.color ?? "inherit"; + return iconColor; + }, + [settings.mimetypes] + ); const columns = React.useMemo( () => [ columnHelper.accessor("mimetype", { - cell: (info) => , - header: () => Type, + cell: (info) => ( + + ), + header: () => , id: "logo", size: 25, enableSorting: false, @@ -191,7 +215,7 @@ function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) { sortingFn: "auto", }), columnHelper.accessor("mimetype", { - cell: (info) => {info.getValue()}, + cell: (info) => {cleanMimetype(info.getValue() ?? "")}, header: () => Type, sortingFn: "alphanumeric", }), diff --git a/frontend/types/custom.d.ts b/frontend/types/custom.d.ts index 5df036a07..870e1133c 100644 --- a/frontend/types/custom.d.ts +++ b/frontend/types/custom.d.ts @@ -36,6 +36,7 @@ declare global { onContextMenuClick: (callback: (id: string) => void) => void; onNavigate: (callback: (url: string) => void) => void; onIframeNavigate: (callback: (url: string) => void) => void; + downloadFile: (path: string) => void; }; type ElectronContextMenuItem = { diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index e31ac039d..f0c33ba7f 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -180,6 +180,7 @@ declare global { // wconfig.MimeTypeConfigType type MimeTypeConfigType = { icon: string; + color: string; }; // waveobj.ORef diff --git a/pkg/wconfig/settingsconfig.go b/pkg/wconfig/settingsconfig.go index 11f74e26b..98d734244 100644 --- a/pkg/wconfig/settingsconfig.go +++ b/pkg/wconfig/settingsconfig.go @@ -39,7 +39,8 @@ type DateTimeFormatConfigType struct { } type MimeTypeConfigType struct { - Icon string `json:"icon"` + Icon string `json:"icon"` + Color string `json:"color"` } type BlockHeaderOpts struct { @@ -67,7 +68,7 @@ func getSettingsConfigDefaults() SettingsConfigType { "audio": {Icon: "file-audio"}, "application/pdf": {Icon: "file-pdf"}, "application/json": {Icon: "file-lines"}, - "directory": {Icon: "folder"}, + "directory": {Icon: "folder", Color: "#2e62d2"}, "font": {Icon: "book-font"}, "image": {Icon: "file-image"}, "text": {Icon: "file-lines"},