From be2be82d7a9a0498e9bde3fa43fa164de3d9919e Mon Sep 17 00:00:00 2001 From: Justin Maximillian Kimlim Date: Wed, 13 Oct 2021 10:20:22 +0700 Subject: [PATCH 1/3] refactor: gather preview handler with file type config --- src/Components/Constants/fileTypes.ts | 36 --- .../ContextMenu/configs/fileMenu.config.ts | 11 +- .../ContextMenu/configs/sidebarMenu.config.ts | 11 +- src/Components/Files/File Operation/open.ts | 9 +- src/Components/Files/File Preview/preview.ts | 145 ++------- src/Components/Files/File Type/type.json | 82 ----- src/Components/Files/File Type/type.ts | 28 +- src/Components/Files/Types/fileTypes.ts | 300 ++++++++++++++++++ src/Components/Layout/home.ts | 11 +- src/Components/Layout/hover.ts | 2 +- src/Components/Properties/properties.ts | 2 +- src/Components/Shortcut/shortcut.ts | 14 +- src/Components/Theme/theme.ts | 2 +- 13 files changed, 372 insertions(+), 281 deletions(-) delete mode 100644 src/Components/Constants/fileTypes.ts delete mode 100644 src/Components/Files/File Type/type.json create mode 100644 src/Components/Files/Types/fileTypes.ts diff --git a/src/Components/Constants/fileTypes.ts b/src/Components/Constants/fileTypes.ts deleted file mode 100644 index 75d88f47..00000000 --- a/src/Components/Constants/fileTypes.ts +++ /dev/null @@ -1,36 +0,0 @@ -const IMAGE_TYPES = [ - '.jpg', - '.png', - '.gif', - '.bmp', - '.jpeg', - '.jpe', - '.jif', - '.jfif', - '.jfi', - '.webp', - '.tiff', - '.tif', - '.ico', - '.svg', - '.webp', -]; -const VIDEO_TYPES = [ - '.mp4', - '.webm', - '.mpg', - '.mp2', - '.mpeg', - '.mpe', - '.mpv', - '.ocg', - '.m4p', - '.m4v', - '.avi', - '.wmv', - '.mov', - '.qt', - '.flv', - '.swf', -]; -export { IMAGE_TYPES, VIDEO_TYPES }; diff --git a/src/Components/ContextMenu/configs/fileMenu.config.ts b/src/Components/ContextMenu/configs/fileMenu.config.ts index 83ea9c3d..7f37800b 100644 --- a/src/Components/ContextMenu/configs/fileMenu.config.ts +++ b/src/Components/ContextMenu/configs/fileMenu.config.ts @@ -1,8 +1,4 @@ -import { - FILE_TYPES_AVAILABLE_FOR_PREVIEW, - Preview, -} from '../../Files/File Preview/preview'; -import path from 'path'; +import { Preview } from '../../Files/File Preview/preview'; import vscodeInstalled from '../../Constants/isVSCodeInstalled'; import contextMenuItem from '../../../Typings/contextMenuItem'; import storage from 'electron-json-storage-sync'; @@ -77,10 +73,7 @@ const FileMenu = ( }, { menu: Translate('Preview'), - visible: - FILE_TYPES_AVAILABLE_FOR_PREVIEW.indexOf( - path.extname(target?.dataset?.path) - ) !== -1, + visible: target?.dataset?.isdir !== 'true', shortcut: 'Ctrl+O', icon: 'preview', role: () => Preview(filePath), diff --git a/src/Components/ContextMenu/configs/sidebarMenu.config.ts b/src/Components/ContextMenu/configs/sidebarMenu.config.ts index bf5eec9b..20190409 100644 --- a/src/Components/ContextMenu/configs/sidebarMenu.config.ts +++ b/src/Components/ContextMenu/configs/sidebarMenu.config.ts @@ -1,13 +1,9 @@ -import { - FILE_TYPES_AVAILABLE_FOR_PREVIEW, - Preview, -} from '../../Files/File Preview/preview'; import contextMenuItem from '../../../Typings/contextMenuItem'; -import path from 'path'; import { createNewTab } from '../../Layout/tab'; import Pin from '../../Files/File Operation/pin'; import { open } from '../../Files/File Operation/open'; import Translate from '../../I18n/i18n'; +import { Preview } from '../../Files/File Preview/preview'; const SidebarMenu = ( target: HTMLElement, @@ -30,11 +26,8 @@ const SidebarMenu = ( }, { menu: Translate('Preview'), - visible: - FILE_TYPES_AVAILABLE_FOR_PREVIEW.indexOf( - path.extname(target?.dataset?.path) - ) !== -1, shortcut: 'Ctrl+O', + visible: target?.dataset?.isdir !== 'true', icon: 'preview', role: () => { Preview(filePath); diff --git a/src/Components/Files/File Operation/open.ts b/src/Components/Files/File Operation/open.ts index b1f87f07..be96b5cf 100644 --- a/src/Components/Files/File Operation/open.ts +++ b/src/Components/Files/File Operation/open.ts @@ -246,7 +246,7 @@ const open = (dir:string, reveal?:boolean):void => { trashPath = fileInfo[1].split('=')[1] trashDeletionDate = fileInfo[2].split("=")[1] } - const type = dirent.isDirectory() ? "File Folder" : getType(unescape(trashPath) ?? path.join(dir, dirent.name)) + const type = getType(unescape(trashPath) ?? path.join(dir, dirent.name), dirent.isDirectory() ) return { name: unescape(trashPath), isDir: dirent.isDirectory(), isHidden: isHiddenFile(path.join(dir, dirent.name)), trashPath, trashDeletionDate, type, isTrash: true, path: unescape(trashPath), realPath: path.join(WINDOWS_TRASH_FILES_PATH, dirent.name) }; }) } @@ -260,7 +260,8 @@ const open = (dir:string, reveal?:boolean):void => { trashPath = fileInfo[1].split('=')[1] trashDeletionDate = fileInfo[2].split("=")[1] } - const type = dirent.isDirectory() ? "File Folder" : getType(unescape(trashPath) ?? path.join(dir, dirent.name)) + const type = getType(unescape(trashPath) ?? path.join(dir, dirent.name), dirent.isDirectory() ) + return { name: unescape(trashPath), isDir: dirent.isDirectory(), isHidden: isHiddenFile(path.join(dir, dirent.name)), trashPath, trashDeletionDate, type, isTrash: true, path: unescape(trashPath), realPath: path.join(LINUX_TRASH_FILES_PATH, dirent.name) }; }) } @@ -295,17 +296,15 @@ const open = (dir:string, reveal?:boolean):void => { const getFiles = () => { return fs.readdirSync(dir, { withFileTypes: true }).map(dirent => { const result:fileData = { name: dirent.name, isDir: dirent.isDirectory(), isHidden: isHiddenFile(path.join(dir, dirent.name)) } - const type = dirent.isDirectory() ? "File Folder" : getType(path.join(dir, dirent.name)) + const type = getType(path.join(dir, dirent.name), dirent.isDirectory()) result.type = type try { const stat = fs.statSync(path.join(dir, dirent.name)) - console.log(stat) result.createdAt = stat.ctime result.modifiedAt = stat.mtime result.accessedAt = stat.atime result.size = stat.size } catch (_) { - console.log('b') if (process.platform === "win32" && !hideSystemFile) { const stat = getAttributesSync(path.join(dir, dirent.name)); if (stat) { diff --git a/src/Components/Files/File Preview/preview.ts b/src/Components/Files/File Preview/preview.ts index ed23a536..6a1b4a1a 100644 --- a/src/Components/Files/File Preview/preview.ts +++ b/src/Components/Files/File Preview/preview.ts @@ -1,16 +1,7 @@ +import { dialog } from '@electron/remote'; import path from 'path'; import { updateTheme } from '../../Theme/theme'; -import mammoth from 'mammoth'; -import fs from 'fs'; -import XLSX from 'xlsx'; -import { URLify, eURLify } from '../../Functions/urlify'; -import hljs from 'highlight.js'; -import marked from 'marked'; -import storage from 'electron-json-storage-sync'; -import { IMAGE_TYPES, VIDEO_TYPES } from '../../Constants/fileTypes'; - -//prettier-ignore -const FILE_TYPES_AVAILABLE_FOR_PREVIEW = ['.pdf', '.html', '.docx', '.htm', '.xlsx', '.xls', '.xlsb', 'xls', '.ods', '.fods', '.csv', '.txt', '.py', '.js', '.bat', '.css', '.c++', '.cpp', '.cc', '.c', '.diff', '.patch', '.go', '.java', '.json', '.php', '.ts', '.tsx', '.jsx', '.jpg', '.png', '.gif', '.bmp', '.jpeg', '.jpe', '.jif', '.jfif', '.jfi', '.webp', '.tiff', '.tif', '.ico', '.svg', '.webp', '.mp4', '.webm', '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv', '.ocg', '.m4p', '.m4v', '.avi', '.wmv', '.mov', '.qt', '.flv', '.swf', '.md'] +import FileTypesConfig from '../Types/fileTypes'; /** * Close the preview file @@ -32,7 +23,9 @@ const Preview = (filePath: string): void => { closePreviewFile(); const previewElement = document.createElement('div'); previewElement.classList.add('preview'); + const changePreview = (html: string) => { + if (!html) return; previewElement.innerHTML = `
${path.basename(filePath)} @@ -52,112 +45,36 @@ const Preview = (filePath: string): void => { .querySelector('.preview-exit-btn') .addEventListener('click', () => closePreviewFile()); }; - if (path.extname(filePath) === '.pdf') { - changePreview( - `` - ); - } else if ( - path.extname(filePath) === '.html' || - path.extname(filePath) === '.htm' - ) { - changePreview( - `` - ); - } else if ( - ['.xlsx', '.xls', '.xlsb', 'xls', '.ods', '.fods', '.csv'].indexOf( - path.extname(filePath) - ) !== -1 - ) { - const xlsxData = XLSX.readFile(filePath); - const parsedData = XLSX.utils.sheet_to_html( - xlsxData.Sheets[xlsxData.SheetNames[0]] - ); - changePreview( - `
${URLify( - parsedData - )}
` - ); - } else if (path.extname(filePath) === '.txt') { - changePreview( - `
${fs - .readFileSync(filePath, 'utf8') - .replaceAll('\n', '
')}
` - ); - } else if (path.extname(filePath) === '.docx') { - mammoth.convertToHtml({ path: filePath }).then(({ value }) => { - changePreview( - eURLify( - `
${value}
` - ) - ); - }); - } else if (IMAGE_TYPES.indexOf(path.extname(filePath)) !== -1) { - changePreview( - `
` - ); - } else if (VIDEO_TYPES.indexOf(path.extname(filePath)) !== -1) { - changePreview( - `
` - ); - } else if (path.extname(filePath) === '.md') { - const parsedData = marked(fs.readFileSync(filePath, 'utf8')); - changePreview( - `
${eURLify( - parsedData - )}
` - ); - } else { - let language; - switch (path.extname(filePath)) { - case '.py': - language = 'python'; - break; - case '.js': - language = 'javascript'; - break; - case '.tx': - language = 'typescript'; - break; - case '.css': - language = 'css'; - break; - case '.cpp': - case '.c++': - case '.cc': - language = 'c++'; - break; - case '.c': - language = 'c'; - break; - case '.diff': - case '.patch': - language = 'diff'; - break; - case '.json': - language = 'json'; - break; + + const ext = filePath.split('.').pop().toLowerCase(); + const basename = path.basename(filePath); + let previewed = false; + for (const type of FileTypesConfig()) { + if ( + (type.fileNames?.indexOf(basename) !== undefined && + type.fileNames?.indexOf(basename) !== -1) || + (type.folderNames?.indexOf(basename) !== undefined && + type.folderNames?.indexOf(basename) !== -1) + ) { + type?.preview?.(filePath, (html) => changePreview(html)); + previewed = true; } - const highlightedCode = language - ? hljs.highlight(fs.readFileSync(filePath, 'utf8'), { - language: language, - }).value //eslint-disable-line no-mixed-spaces-and-tabs - : hljs.highlightAuto(fs.readFileSync(filePath, 'utf8')).value; - changePreview( - `
${highlightedCode}
` - ); } - const recents = storage.get('recent')?.data; - - // Push file into recent files - if (recents) { - if (recents.indexOf(filePath) !== -1) { - recents.push(recents.splice(recents.indexOf(filePath), 1)[0]); - storage.set('recent', recents); - } else { - storage.set('recent', [...recents, filePath]); + for (const type of FileTypesConfig()) { + if ( + type.extension?.indexOf(ext) !== undefined && + type.extension?.indexOf(ext) !== -1 + ) { + type?.preview?.(filePath, (html) => changePreview(html)); + previewed = true; } - } else storage.set('recent', [filePath]); + } + if (!previewed) + dialog.showErrorBox( + 'No preview handler', + 'There is no preview handler for this file type yet.' + ); updateTheme(); }; -export { Preview, FILE_TYPES_AVAILABLE_FOR_PREVIEW, closePreviewFile }; +export { Preview, closePreviewFile }; diff --git a/src/Components/Files/File Type/type.json b/src/Components/Files/File Type/type.json deleted file mode 100644 index 7bdf83d5..00000000 --- a/src/Components/Files/File Type/type.json +++ /dev/null @@ -1,82 +0,0 @@ -[ - { - "extension": ["js"], - "type": "JavaScript" - }, - { - "extension": ["ts"], - "type": "TypeScript" - }, - { - "extension": ["html", "htm", "xhtml", "html_vm"], - "type": "HyperText Markup Language" - }, - { - "extension": ["pdf"], - "type": "Portable Document Format" - }, - { - "extension": ["doc", "docb", "docm", "dot", "dotm", "docx"], - "type": "Word Document" - }, - { - "extension": ["xlm", "xls", "xlsx", "xlt"], - "type": "Excel Document" - }, - { - "extension": ["pot", "potm", "potx", "ppam", "pps", "ppsm", "ppsx", "ppt", "pptn", "pptx"], - "type": "Powerpoint Document" - }, - { - "extension": ["7z", "brotli", "bzip2", "gz", "gzip", "rar", "tgz", "xz", "zip"], - "type": "Archive" - }, - { - "extension": ["accdb", "db", "db3", "mdb", "pdb", "pgsql", "pkb", "pks", "postgres", "psql", "sql", "sqlite", "sqlite3"], - "type": "Database" - }, - { - "extension": ["bat"], - "type": "Batch" - }, - { - "extension": ["exe"], - "type": "Executable" - }, - { - "extension": ["c++", "cc", "cpp", "cp"], - "type": "C++ Program" - }, - { - "extension": ["c"], - "type": "C Program" - }, - { - "extension": ["h"], - "type": "C Header File" - }, - { - "extension": ["py", "py3"], - "type": "Python Program" - }, - { - "extension": ["txt"], - "type": "Text Document" - }, - { - "extension": ["jpg", "png", "gif", "bmp", "jpeg", "jpe", "jif", "jfif", "jfi", "webp", "tiff", "tif", "ico", "svg", "webp"], - "type": "Image" - }, - { - "extension": ["mp4", "webm", "mpg", "mp2", "mpeg", "mpe", "mpv", "ocg", "m4p", "m4v", "avi", "wmv", "mov", "qt", "flv", "swf"], - "type": "Video" - }, - { - "extension": ["deb", "msi", "snap"], - "type": "Installer" - }, - { - "extension": ["iso"], - "type": "Disk Image File" - } -] \ No newline at end of file diff --git a/src/Components/Files/File Type/type.ts b/src/Components/Files/File Type/type.ts index 3748da4d..bd49e93b 100644 --- a/src/Components/Files/File Type/type.ts +++ b/src/Components/Files/File Type/type.ts @@ -1,16 +1,34 @@ -import Types from './type.json'; import path from 'path'; +import FileTypesConfig from '../Types/fileTypes'; /** * Get type of a file name * @param {string} filename - File name + * @param {boolean} isDir - is it a directory? * @returns {string} File Type */ -const getType = (filename: string): string => { +const getType = (filename: string, isDir?: boolean): string => { + filename = path.basename(filename); const ext = filename.split('.').pop().toLowerCase(); - for (const type of Types) { - if (type.extension.indexOf(ext) !== -1) return type.type; + // Prioritize exact file name and folder name over file extension + for (const type of FileTypesConfig()) { + if ( + (type.fileNames?.indexOf(filename) !== undefined && + type.fileNames?.indexOf(filename) !== -1) || + (type.folderNames?.indexOf(filename) !== undefined && + type.folderNames?.indexOf(filename) !== -1) + ) { + return type.type; + } } - return `${path.basename(ext).toUpperCase()} file`; + for (const type of FileTypesConfig()) { + if ( + type.extension?.indexOf(ext) !== undefined && + type.extension?.indexOf(ext) !== -1 + ) { + return type.type; + } + } + return isDir ? 'File Folder' : `${path.basename(ext).toUpperCase()} file`; }; export default getType; diff --git a/src/Components/Files/Types/fileTypes.ts b/src/Components/Files/Types/fileTypes.ts new file mode 100644 index 00000000..bbb7b4ed --- /dev/null +++ b/src/Components/Files/Types/fileTypes.ts @@ -0,0 +1,300 @@ +import { URLify, eURLify } from '../../Functions/urlify'; +import fs from 'fs'; + +interface FileTypes { + extension?: string[]; + fileNames?: string[]; + folderNames?: string[]; + type: string; + preview?: (filePath: string, cb: (html: string) => void) => void; +} + +const IMAGE_TYPES = [ + 'jpg', + 'png', + 'gif', + 'bmp', + 'jpeg', + 'jpe', + 'jif', + 'jfif', + 'jfi', + 'webp', + 'tiff', + 'tif', + 'ico', + 'svg', + 'webp', +]; +const VIDEO_TYPES = [ + 'mp4', + 'webm', + 'mpg', + 'mp2', + 'mpeg', + 'mpe', + 'mpv', + 'ocg', + 'm4p', + 'm4v', + 'avi', + 'wmv', + 'mov', + 'qt', + 'flv', + 'swf', +]; + +const previewCode = (filePath: string, language?: string) => { + const hljs = require('highlight.js'); + const highlightedCode = language + ? hljs.highlight(fs.readFileSync(filePath, 'utf8'), { + language: language, + }).value //eslint-disable-line no-mixed-spaces-and-tabs + : hljs.highlightAuto(fs.readFileSync(filePath, 'utf8')).value; + return `
${highlightedCode}
`; +}; + +const FileTypesConfig = (): FileTypes[] => { + return [ + { + extension: ['js'], + type: 'JavaScript', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'javascript')), + }, + { + extension: ['ts'], + type: 'TypeScript', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'typescript')), + }, + { + extension: ['html', 'htm', 'xhtml', 'html_vm'], + type: 'HyperText Markup Language', + preview: (filePath: string, cb: (html: string) => void) => + cb( + `` + ), + }, + { + extension: ['pdf'], + type: 'Portable Document Format', + preview: (filePath: string, cb: (html: string) => void) => { + cb( + `` + ); + }, + }, + { + extension: ['doc', 'docb', 'docm', 'dot', 'dotm', 'docx'], + type: 'Word Document', + preview: (filePath: string, cb: (html: string) => void) => { + if (filePath.endsWith('docx')) { + const mammoth = require('mammoth'); + mammoth + .convertToHtml({ path: filePath }) + .then(({ value }: { value: string }) => { + cb( + eURLify( + `
${value}
` + ) + ); + }); + } + }, + }, + { + extension: ['xlsx', 'xls', 'xlsb', 'xls', 'ods', 'fods', 'csv'], + type: 'Excel Document', + preview: (filePath: string, cb: (html: string) => void) => { + const XLSX = require('xlsx'); + const xlsxData = XLSX.readFile(filePath); + const parsedData = XLSX.utils.sheet_to_html( + xlsxData.Sheets[xlsxData.SheetNames[0]] + ); + cb( + `
${URLify( + parsedData + )}
` + ); + }, + }, + { + extension: [ + 'pot', + 'potm', + 'potx', + 'ppam', + 'pps', + 'ppsm', + 'ppsx', + 'ppt', + 'pptn', + 'pptx', + ], + type: 'Powerpoint Document', + }, + { + extension: [ + '7z', + 'brotli', + 'bzip2', + 'gz', + 'gzip', + 'rar', + 'tgz', + 'xz', + 'zip', + ], + type: 'Archive', + }, + { + extension: [ + 'accdb', + 'db', + 'db3', + 'mdb', + 'pdb', + 'pgsql', + 'pkb', + 'pks', + 'postgres', + 'psql', + 'sql', + 'sqlite', + 'sqlite3', + ], + type: 'Database', + }, + { + extension: ['bat'], + type: 'Batch', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'bat')), + }, + { + extension: ['exe'], + type: 'Executable', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'bat')), + }, + { + extension: ['c++', 'cc', 'cpp', 'cp'], + type: 'C++ Program', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'c++')), + }, + { + extension: ['c'], + type: 'C Program', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'c')), + }, + { + extension: ['h'], + type: 'C Header File', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'c')), + }, + { + extension: ['py', 'py3'], + type: 'Python Program', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'python')), + }, + { + extension: ['txt'], + type: 'Text Document', + preview: (filePath: string, cb: (html: string) => void) => { + cb( + `
${fs + .readFileSync(filePath, 'utf8') + .replaceAll('\n', '
')}
` + ); + }, + }, + { + extension: IMAGE_TYPES, + type: 'Image', + preview: (filePath: string, cb: (html: string) => void) => + cb( + `
` + ), + }, + { + extension: VIDEO_TYPES, + type: 'Video', + preview: (filePath: string, cb: (html: string) => void) => + cb( + `
` + ), + }, + { + extension: ['deb', 'msi', 'snap'], + type: 'Installer', + }, + { + extension: ['iso'], + type: 'Disk Image File', + }, + { + extension: ['Dockerfile'], + fileNames: ['Dockerfile'], + type: 'Docker Image', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'docker')), + }, + { + extension: ['md', 'markdown'], + type: 'Markdown', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'md')), + }, + { + extension: ['json'], + type: 'JavaScript Object Notation', + preview: (filePath: string, cb: (html: string) => void) => { + cb(previewCode(filePath, 'json')); + }, + }, + { + folderNames: ['.git'], + type: 'Git', + fileNames: ['gitignore', 'gitconfig'], + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath)), + }, + { + fileNames: ['yml', 'yaml'], + type: 'Yet Anoter Markup Language', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'yml')), + }, + { + folderNames: ['node_modules'], + type: 'Node Packages', + }, + { + fileNames: ['.prettierignore', '.prettierrc.json'], + type: 'Prettier configuration file', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath)), + }, + { + fileNames: ['binding.gyp'], + type: 'Node JS C++ Binding', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath)), + }, + { + fileNames: ['LICENSE', 'LICENSE.md', 'LICENSE.txt'], + type: 'License', + preview: (filePath: string, cb: (html: string) => void) => + cb(previewCode(filePath, 'md')), + }, + ]; +}; + +export { IMAGE_TYPES, VIDEO_TYPES }; +export default FileTypesConfig; diff --git a/src/Components/Layout/home.ts b/src/Components/Layout/home.ts index 29a21a86..e58b9024 100644 --- a/src/Components/Layout/home.ts +++ b/src/Components/Layout/home.ts @@ -51,9 +51,10 @@ const homeFiles = (callback: cb) => { ), displayName: dirent.name, }; - const type = dirent.isDirectory() - ? 'File Folder' - : getType(path.join(os.homedir(), dirent.name)); + const type = getType( + path.join(os.homedir(), dirent.name), + dirent.isDirectory() + ); result.type = type; const stat = fs.statSync( path.join(os.homedir(), dirent.name) @@ -124,9 +125,7 @@ const homeFiles = (callback: cb) => { } result += `
{ if (process.platform === 'win32') getAttributesSync = require('fswin').getAttributesSync; //eslint-disable-line isHiddenFile = checkIsHiddenFile(filePath); //eslint-disable-line - fileType = isDirectory ? 'File Folder' : getType(filePath); + fileType = getType(filePath, isDirectory); try { const stat = fs.statSync(filePath); createdAt = stat.ctime; diff --git a/src/Components/Shortcut/shortcut.ts b/src/Components/Shortcut/shortcut.ts index d9153bba..e4363744 100644 --- a/src/Components/Shortcut/shortcut.ts +++ b/src/Components/Shortcut/shortcut.ts @@ -7,16 +7,12 @@ import { toggleHideHiddenFilesValue, getHideHiddenFilesValue, } from '../Functions/toggleHiddenFiles'; -import path from 'path'; import Copy from '../Files/File Operation/copy'; import Cut from '../Files/File Operation/cut'; import Paste from '../Files/File Operation/paste'; import Pin from '../Files/File Operation/pin'; import { Trash, PermanentDelete } from '../Files/File Operation/trash'; -import { - Preview, - FILE_TYPES_AVAILABLE_FOR_PREVIEW, -} from '../Files/File Preview/preview'; +import { Preview } from '../Files/File Preview/preview'; import windowGUID from '../Constants/windowGUID'; import remote from '@electron/remote'; import focusingPath from '../Functions/focusingPath'; @@ -221,13 +217,7 @@ const Shortcut = (): void => { } // Preview file shortcut (Ctrl+O) else if (e.ctrlKey && e.key === 'o') { - if ( - FILE_TYPES_AVAILABLE_FOR_PREVIEW.indexOf( - path.extname(selectedFilePath) - ) !== -1 - ) { - Preview(selectedFilePath); - } + Preview(selectedFilePath); } // File properties (Ctrl+P) else if (e.ctrlKey && e.key === 'p') { diff --git a/src/Components/Theme/theme.ts b/src/Components/Theme/theme.ts index 4bf4b223..3f1e0f5d 100644 --- a/src/Components/Theme/theme.ts +++ b/src/Components/Theme/theme.ts @@ -22,7 +22,7 @@ interface Theme{ let defaultTheme = detectDefaultTheme(); let developmentStylesheet; // this is important, as this will store the custom stylesheet during development path. -const updateNativeTheme = () => { +const updateNativeTheme = ():void => { defaultTheme = detectDefaultTheme() ipcRenderer.send('update-theme'); updateTheme() From 0ca88192464629b78747ed84f5da9ea67defa391 Mon Sep 17 00:00:00 2001 From: Justin Maximillian Kimlim Date: Wed, 13 Oct 2021 13:48:03 +0700 Subject: [PATCH 2/3] refactor: simplify file thumbnail code --- .prettierignore | 1 - .../fileTypes.ts => Config/file.config.ts} | 38 ++- src/Components/Config/folder.config.ts | 130 +++++++++ src/Components/ContextMenu/contextMenu.ts | 6 +- src/Components/Drives/drives.ts | 4 +- src/Components/Favorites/favorites.ts | 14 +- src/Components/Files/File Icon/fileIcon.ts | 137 --------- src/Components/Files/File Icon/icon.json | 273 ------------------ src/Components/Files/File Operation/open.ts | 4 +- src/Components/Files/File Preview/preview.ts | 12 +- src/Components/Files/File Type/type.ts | 21 +- src/Components/Functions/extractExeIcon.ts | 33 +++ src/Components/Layout/home.ts | 4 +- src/Components/Layout/hover.ts | 2 +- src/Components/Layout/sidebar.ts | 12 +- src/Components/Recent/recent.ts | 4 +- src/Components/Setting/setting.ts | 4 +- src/Components/Thumbnail/thumbnail.ts | 117 ++++++++ src/Icon/css.svg | 3 - .../about.svg => folder/setting-about.svg} | 0 .../setting-appearance.svg} | 0 .../setting-preference.svg} | 0 .../sidebar-desktop.svg} | 0 .../sidebar-document.svg} | 0 .../sidebar-download.svg} | 0 .../sidebar-favorite.svg} | 0 .../home.svg => folder/sidebar-home.svg} | 0 .../music.svg => folder/sidebar-music.svg} | 0 .../sidebar-picture.svg} | 0 .../recent.svg => folder/sidebar-recent.svg} | 0 .../sidebar-setting.svg} | 0 .../trash.svg => folder/sidebar-trash.svg} | 0 .../video.svg => folder/sidebar-video.svg} | 0 33 files changed, 354 insertions(+), 465 deletions(-) rename src/Components/{Files/Types/fileTypes.ts => Config/file.config.ts} (87%) create mode 100644 src/Components/Config/folder.config.ts delete mode 100644 src/Components/Files/File Icon/fileIcon.ts delete mode 100644 src/Components/Files/File Icon/icon.json create mode 100644 src/Components/Functions/extractExeIcon.ts create mode 100644 src/Components/Thumbnail/thumbnail.ts delete mode 100644 src/Icon/css.svg rename src/Icon/{settings/about.svg => folder/setting-about.svg} (100%) rename src/Icon/{settings/appearance.svg => folder/setting-appearance.svg} (100%) rename src/Icon/{settings/preference.svg => folder/setting-preference.svg} (100%) rename src/Icon/{sidebar/desktop.svg => folder/sidebar-desktop.svg} (100%) rename src/Icon/{sidebar/document.svg => folder/sidebar-document.svg} (100%) rename src/Icon/{sidebar/download.svg => folder/sidebar-download.svg} (100%) rename src/Icon/{sidebar/favorite.svg => folder/sidebar-favorite.svg} (100%) rename src/Icon/{sidebar/home.svg => folder/sidebar-home.svg} (100%) rename src/Icon/{sidebar/music.svg => folder/sidebar-music.svg} (100%) rename src/Icon/{sidebar/picture.svg => folder/sidebar-picture.svg} (100%) rename src/Icon/{sidebar/recent.svg => folder/sidebar-recent.svg} (100%) rename src/Icon/{sidebar/settings.svg => folder/sidebar-setting.svg} (100%) rename src/Icon/{sidebar/trash.svg => folder/sidebar-trash.svg} (100%) rename src/Icon/{sidebar/video.svg => folder/sidebar-video.svg} (100%) diff --git a/.prettierignore b/.prettierignore index fd7a1e5c..acd746aa 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,7 +3,6 @@ dist outs node_modules src/Components/Theme/theme.ts -src/Components/Files/File Icon/fileIcon.ts src/Components/Files/File Operation/open.ts src/Components/Favorites/favorites.ts *.min. diff --git a/src/Components/Files/Types/fileTypes.ts b/src/Components/Config/file.config.ts similarity index 87% rename from src/Components/Files/Types/fileTypes.ts rename to src/Components/Config/file.config.ts index bbb7b4ed..0e0e1d33 100644 --- a/src/Components/Files/Types/fileTypes.ts +++ b/src/Components/Config/file.config.ts @@ -1,12 +1,12 @@ -import { URLify, eURLify } from '../../Functions/urlify'; +import { URLify, eURLify } from '../Functions/urlify'; import fs from 'fs'; -interface FileTypes { +interface FileConfigType { extension?: string[]; fileNames?: string[]; - folderNames?: string[]; type: string; preview?: (filePath: string, cb: (html: string) => void) => void; + thumbnail?: (filePath?: string) => string; } const IMAGE_TYPES = [ @@ -55,22 +55,25 @@ const previewCode = (filePath: string, language?: string) => { return `
${highlightedCode}
`; }; -const FileTypesConfig = (): FileTypes[] => { +const FileConfig = (): FileConfigType[] => { return [ { extension: ['js'], type: 'JavaScript', + thumbnail: () => 'extension/javascript.svg', preview: (filePath: string, cb: (html: string) => void) => cb(previewCode(filePath, 'javascript')), }, { extension: ['ts'], type: 'TypeScript', + thumbnail: () => 'extension/file.svg', preview: (filePath: string, cb: (html: string) => void) => cb(previewCode(filePath, 'typescript')), }, { extension: ['html', 'htm', 'xhtml', 'html_vm'], + thumbnail: () => 'extension/html.svg', type: 'HyperText Markup Language', preview: (filePath: string, cb: (html: string) => void) => cb( @@ -80,6 +83,7 @@ const FileTypesConfig = (): FileTypes[] => { { extension: ['pdf'], type: 'Portable Document Format', + thumbnail: () => 'extension/pdf.svg', preview: (filePath: string, cb: (html: string) => void) => { cb( `` @@ -176,6 +180,24 @@ const FileTypesConfig = (): FileTypes[] => { { extension: ['exe'], type: 'Executable', + thumbnail: (filePath: string) => { + const storage = require('electron-json-storage-sync'); + const preference = storage.get('preference')?.data; + const extractExeIcon = + require('../Functions/extractExeIcon').default; + try { + if ( + (preference?.extractExeIcon ?? false) && + process.platform === 'win32' + ) { + return extractExeIcon(filePath); + } + } catch (err) { + console.log(err); + return 'extension/exe.svg'; + } + extractExeIcon(filePath); + }, preview: (filePath: string, cb: (html: string) => void) => cb(previewCode(filePath, 'bat')), }, @@ -217,6 +239,7 @@ const FileTypesConfig = (): FileTypes[] => { { extension: IMAGE_TYPES, type: 'Image', + thumbnail: (filePath: string) => filePath, preview: (filePath: string, cb: (html: string) => void) => cb( `
` @@ -259,7 +282,6 @@ const FileTypesConfig = (): FileTypes[] => { }, }, { - folderNames: ['.git'], type: 'Git', fileNames: ['gitignore', 'gitconfig'], preview: (filePath: string, cb: (html: string) => void) => @@ -271,10 +293,6 @@ const FileTypesConfig = (): FileTypes[] => { preview: (filePath: string, cb: (html: string) => void) => cb(previewCode(filePath, 'yml')), }, - { - folderNames: ['node_modules'], - type: 'Node Packages', - }, { fileNames: ['.prettierignore', '.prettierrc.json'], type: 'Prettier configuration file', @@ -297,4 +315,4 @@ const FileTypesConfig = (): FileTypes[] => { }; export { IMAGE_TYPES, VIDEO_TYPES }; -export default FileTypesConfig; +export default FileConfig; diff --git a/src/Components/Config/folder.config.ts b/src/Components/Config/folder.config.ts new file mode 100644 index 00000000..7cb0c431 --- /dev/null +++ b/src/Components/Config/folder.config.ts @@ -0,0 +1,130 @@ +interface folderThumbnailType { + folderNames: string[]; + thumbnail?: string; + type?: string; +} +const folderConfig = (): folderThumbnailType[] => { + return [ + { + folderNames: [ + 'document', + 'documents', + 'article', + 'articles', + 'documentation', + 'docs', + 'post', + 'posts', + ], + thumbnail: 'folder/folder-document.svg', + }, + { + folderNames: ['download', 'downloads'], + thumbnail: 'folder/folder-download.svg', + }, + { + folderNames: [ + 'picture', + 'pictures', + 'ico', + 'icon', + 'image', + 'icons', + 'images', + 'img', + 'screenshot', + 'screenshots', + ], + thumbnail: 'folder/folder-picture.svg', + }, + { + folderNames: ['music'], + thumbnail: 'folder/folder-music.svg', + }, + { + folderNames: ['desktop'], + thumbnail: 'folder/folder-desktop.svg', + }, + { + folderNames: ['video', 'videos'], + thumbnail: 'folder/folder-video.svg', + }, + { + folderNames: ['home'], + thumbnail: 'folder/folder-home.svg', + }, + { + folderNames: ['trash'], + thumbnail: 'folder/folder-trash.svg', + }, + { + folderNames: ['setting', 'settings'], + thumbnail: 'folder/folder-setting.svg', + }, + { + folderNames: ['recent', 'recents'], + thumbnail: 'folder/folder-recent.svg', + }, + { + folderNames: ['favorite', 'favorites'], + thumbnail: 'folder/folder-favorite.svg', + }, + { + folderNames: ['about'], + thumbnail: 'folder/setting-about.svg', + }, + { + folderNames: ['appearance'], + thumbnail: 'folder/setting-appearance.svg', + }, + { + folderNames: ['preference'], + thumbnail: 'folder/setting-preference.svg', + }, + { + folderNames: ['.git', 'git'], + thumbnail: 'folder/folder-git.svg', + type: 'Version Control System', + }, + { + folderNames: ['android'], + thumbnail: 'folder/folder-android.svg', + }, + { + folderNames: ['node_modules'], + type: 'Node Package Modules', + thumbnail: 'folder/folder-node.svg', + }, + ]; +}; +const defaultThumbnail = { + file: 'file.svg', + folder: 'folder.svg', + image: 'extension/image.svg', + video: 'extension/video.svg', +}; + +interface customThumbnailType { + [key: string]: string; +} +const customThumbnail: customThumbnailType = { + 'sidebar-about': 'folder/sidebar-about.svg', + 'sidebar-recent': 'folder/sidebar-recent.svg', + 'sidebar-favorites': 'folder/sidebar-favorite.svg', + 'sidebar-setting': 'folder/sidebar-setting.svg', + 'sidebar-trash': 'folder/sidebar-trash.svg', + 'sidebar-desktop': 'folder/sidebar-desktop.svg', + 'sidebar-home': 'folder/sidebar-home.svg', + 'sidebar-documents': 'folder/sidebar-document.svg', + 'sidebar-downloads': 'folder/sidebar-download.svg', + 'sidebar-music': 'folder/sidebar-music.svg', + 'sidebar-pictures': 'folder/sidebar-picture.svg', + 'sidebar-videos': 'folder/sidebar-video.svg', + 'settings-about': 'folder/setting-about.svg', + 'settings-appearance': 'folder/setting-appearance.svg', + 'settings-preference': 'folder/setting-preference.svg', + 'favorites-usb': 'usb.svg', + 'favorites-hard-disk': 'hard-disk.svg', +}; +export default folderConfig; +export { defaultThumbnail, customThumbnail }; diff --git a/src/Components/ContextMenu/contextMenu.ts b/src/Components/ContextMenu/contextMenu.ts index 71008678..b6958838 100644 --- a/src/Components/ContextMenu/contextMenu.ts +++ b/src/Components/ContextMenu/contextMenu.ts @@ -3,7 +3,7 @@ import SidebarDriveMenu from './configs/sidebarDriveMenu.config'; import BodyMenu from './configs/bodyMenu.config'; import MultipleSelectedMenu from './configs/multipleSelectedMenu.config'; import contextMenuItem from '../../Typings/contextMenuItem'; -import fileIcon from '../Files/File Icon/fileIcon'; +import fileThumbnail from '../Thumbnail/thumbnail'; import { getSelected } from '../Files/File Operation/select'; import { updateTheme } from '../Theme/theme'; import FileMenu from './configs/fileMenu.config'; @@ -30,7 +30,7 @@ const MenuToElements = (menu: contextMenuItem[][]) => { if (item.icon) { if (item.shortcut) - menu.innerHTML = ` { item.shortcut }`; else - menu.innerHTML = ` {
- { const result = `

${Translate("Favorites")}

-

Desktop icon${Translate("Desktop")}

+

Desktop icon${Translate("Desktop")}

-

Document icon${Translate("Documents")}

+

Document icon${Translate("Documents")}

-

Download icon${Translate("Downloads")}

+

Download icon${Translate("Downloads")}

-

Pictures icon${Translate("Pictures")}

+

Pictures icon${Translate("Pictures")}

-

Music icon${Translate("Music")}

+

Music icon${Translate("Music")}

-

Video icon${Translate("Videos")}

+

Video icon${Translate("Videos")}

`; return result; diff --git a/src/Components/Files/File Icon/fileIcon.ts b/src/Components/Files/File Icon/fileIcon.ts deleted file mode 100644 index fc63ef07..00000000 --- a/src/Components/Files/File Icon/fileIcon.ts +++ /dev/null @@ -1,137 +0,0 @@ -import fs from "fs"; -import path from "path"; -import storage from 'electron-json-storage-sync'; -import electron from "electron"; -import defaultIconData from "./icon.json"; -import { extractIcon } from "../../../Lib/extracticon/bindings"; -interface Icon{ - [key:string]: { - [key:string]: string - } -} - -let iconJSON:Icon = null; -let iconJSONPath:string = null; -const defaultIconJSON:Icon = defaultIconData; - -// Load cache -const icon = storage.get('icon') - -const IMAGE = ['jpg', 'png', 'gif', 'bmp', 'jpeg', 'jpe', 'jif', 'jfif', 'jfi', 'webp', 'tiff', 'tif', 'ico', 'svg', 'webp']; -const VIDEO = ['mp4', 'webm', 'mpg', 'mp2', 'mpeg', 'mpe', 'mpv', 'ocg', 'm4p', 'm4v', 'avi', 'wmv', 'mov', 'qt', 'flv', 'swf']; - -const DEFAULT_FILE_ICON = path.join(__dirname, "../../../icon", defaultIconJSON.default.file) -const DEFAULT_FOLDER_ICON = path.join(__dirname, "../../../icon", defaultIconJSON.default.folder) - -let DEFAULT_IMAGE = path.join(__dirname, '../../../icon', defaultIconJSON.file.image) - -/** - * Return image view of preview - * @param {string} filename - the file name - * @param {boolean} isdir - is it directory? - * @returns {string} HTML Result - */ -const iconPreview = (filename:string, isdir?:boolean) => { - return `` -} - -/** - * Return video view of preview - * @param {string} filename - * @returns {string} HTML Result - */ -const videoPreview = (filename:string) => { - const preference = storage.get("preference")?.data - let alt = path.join(iconJSONPath || __dirname, iconJSON ? '../' : '../../../Icon', iconJSON?.file?.video || defaultIconJSON.file.video) // Alternative for video if video could not be oaded - if (!fs.existsSync(alt)) alt = path.join(__dirname, '../../../Icon/', defaultIconJSON.default.file) - return preference?.autoPlayPreviewVideo ? `` : iconPreview(alt, false) -} - -/** - * Extract EXE icon and return it as preview - * @param {string} filename - EXE file name - * @returns {any} the path of icon extracted from the EXE file - */ -const exePreview = (filename:string) => { - const basename = filename.split(/[\\/]/)[filename.split(/[\\/]/).length - 1] - const app = electron.app || (electron.remote && electron.remote.app) || null; - const EXE_ICON_CACHE_DIR = path.join(app.getPath('userData'), 'Cache/Exe Icon'); - - // Create cache directory if not exist - if (!fs.existsSync(EXE_ICON_CACHE_DIR)) { - if (!fs.existsSync(path.join(EXE_ICON_CACHE_DIR, '..'))) { - fs.mkdirSync(path.join(EXE_ICON_CACHE_DIR, '..')) - } - fs.mkdirSync(EXE_ICON_CACHE_DIR) - } - const ICON_FILE_NAME = path.join(EXE_ICON_CACHE_DIR, basename + '.ico') - - // Cache the icon parsed from the exe - if (fs.existsSync(ICON_FILE_NAME)) { - return iconPreview(ICON_FILE_NAME) - } else { - const buffer = extractIcon(filename, 'large'); - fs.writeFileSync(ICON_FILE_NAME, buffer); - return iconPreview(ICON_FILE_NAME) - } -} - -/** - * Get file icon of a file/folder - * @param {string} filename - name of the file/folder - * @param {string} category - category of the file/folder (optional) - * @param {boolean} HTMLFormat - return with the HTML format (optional) - * @returns {string} the preview of the file/folder - */ -const fileIcon = (filename:string, category = "folder", HTMLFormat = true):string => { - const preference = storage.get("preference")?.data; - const extractExeIcon = preference?.extractExeIcon ?? false; - if (icon.data && fs.existsSync(icon.data.iconJSON)) { - iconJSON = JSON.parse(fs.readFileSync(icon.data.iconJSON, 'utf-8')) - iconJSONPath = icon.data.iconJSON - } - - const ext = filename.split('.').pop().toLowerCase() // Get extension of filename - const source = iconJSON || defaultIconJSON - - if (IMAGE.indexOf(ext) !== -1) return HTMLFormat ? iconPreview(filename, false) : filename // Show the image itself if the file is image - else if (VIDEO.indexOf(ext) !== -1) return HTMLFormat ? videoPreview(filename) : filename // Show the video itself if the file is video - - try { - if (ext === "exe" && extractExeIcon && process.platform === "win32") return exePreview(filename) - // eslint-disable-next-line no-empty - } catch (err) {console.log(err)} - - filename = filename.toLowerCase() // Lowercase filename - - const folderName = filename.split(/[\\/]/).pop() - - const categoryObj = category === "file" ? ext : folderName - - if (fs.existsSync(iconJSON?.file?.image)) DEFAULT_IMAGE = path.join(icon?.data?.iconJSON, '../', iconJSON.file.image) - - // Check if there's a icon for the folder name - if (Object.keys(iconJSON?.[category] || defaultIconJSON[category]).indexOf(categoryObj) !== -1) { - let fileLoc; - // Check if category exist in user preference JSON file - if (category in source) { - fileLoc = (source?.[category][categoryObj][0] === "/") ? source[category][categoryObj] : path.join(icon?.data?.iconJSON || __dirname, iconJSON ? '../' : '../../../Icon/', source[category][categoryObj]) - } - if (fs.existsSync(fileLoc)) return HTMLFormat ? iconPreview(fileLoc, category === "folder") : fileLoc - else { - fileLoc = iconJSON?.default?.[category === "file" ? "file" : "folder"] && fs.existsSync(iconJSON?.default?.[category === "file" ? "file" : "folder"]) ? path.join(iconJSONPath, '../', iconJSON?.default?.[category === "file" ? "file" : "folder"]) : path.join(__dirname, '../../../icon/', defaultIconJSON.default[category === "file" ? "file" : "folder"]) - return HTMLFormat ? iconPreview(fileLoc, category === "folder") : fileLoc - } - } else { - let fileLoc; - // Check if category exist in user preference JSON file - if ("default" in source) { - fileLoc = (source.default[category === "file" ? "file" : "folder"][0] === "/") ? source.default[category === "file" ? "file" : "folder"] : path.join(icon?.data?.iconJSON || __dirname, iconJSON ? '../' : '../../../Icon/', source.default[category === "file" ? "file" : "folder"]) - } - if (fs.existsSync(fileLoc)) return HTMLFormat ? iconPreview(fileLoc, category === "folder") : fileLoc - else return HTMLFormat ? iconPreview(path.join(__dirname, "../../../Icon/", iconJSON?.default?.[category === "file" ? "file" : "folder"] || defaultIconJSON.default[category === "file" ? "file" : "folder"]), category === "folder") : path.join(__dirname, "../../../Icon/", iconJSON?.default?.[category === "file" ? "file" : "folder"] || defaultIconJSON.default[category === "file" ? "file" : "folder"]) - - } -} - -export default fileIcon \ No newline at end of file diff --git a/src/Components/Files/File Icon/icon.json b/src/Components/Files/File Icon/icon.json deleted file mode 100644 index 3921229f..00000000 --- a/src/Components/Files/File Icon/icon.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "default": { - "file": "file.svg", - "folder": "folder.svg" - }, - "favorites": { - "desktop": "folder/folder-desktop.svg", - "document": "folder/folder-document.svg", - "download": "folder/folder-download.svg", - "music": "folder/folder-music.svg", - "picture": "folder/folder-picture.svg", - "usb": "usb.svg", - "hard-disk": "hard-disk.svg", - "video": "folder/folder-video.svg" - }, - "file": { - "7z": "extension/zip.svg", - "accdb": "extension/database.svg", - "asp": "extension/html.svg", - "bat": "extension/bat.svg", - "brotli": "extension/zip.svg", - "bzip2": "extension/zip.svg", - "c": "extension/c.svg", - "c++": "extension/cpp.svg", - "cc": "extension/cpp.svg", - "cfg": "extension/settings.svg", - "conf": "extension/settings.svg", - "config": "extension/settings.svg", - "cp": "extension/cpp.svg", - "cpp": "extension/cpp.svg", - "csproj": "extension/visualstudio.svg", - "css": "extension/css.svg", - "cxx": "extension/cpp.svg", - "db": "extension/database.svg", - "db3": "extension/database.svg", - "dlc": "extension/settings.svg", - "doc": "extension/word.svg", - "docb": "extension/word.svg", - "docm": "extension/word.svg", - "docx": "extension/word.svg", - "dot": "extension/word.svg", - "dotm": "extension/word.svg", - "exe": "extension/exe.svg", - "go": "extension/go.svg", - "gz": "extension/zip.svg", - "gzip": "extension/zip.svg", - "h": "extension/h.svg", - "htm": "extension/html.svg", - "html": "extension/html.svg", - "html_vm": "extension/html.svg", - "i": "extension/c.svg", - "ii": "extension/cpp.svg", - "image": "extension/image.svg", - "ini": "extension/settings.svg", - "jar": "extension/java.svg", - "java": "extension/java.svg", - "js": "extension/javascript.svg", - "json": "extension/json.svg", - "json5": "extension/json.svg", - "jsonl": "extension/json.svg", - "jsp": "extension/jsp.svg", - "jsx": "extension/react.svg", - "m": "extension/c.svg", - "markdown": "extension/markdown.svg", - "md": "extension/markdown.svg", - "mdb": "extension/database.svg", - "mi": "extension/c.svg", - "mii": "extension/cpp.svg", - "mm": "extension/cpp.svg", - "msi": "extension/exe.svg", - "ndjson": "extension/json.svg", - "option": "extension/settings.svg", - "pdb": "extension/database.svg", - "pdf": "extension/pdf.svg", - "pgsql": "extension/database.svg", - "pkb": "extension/database.svg", - "pks": "extension/database.svg", - "postgres": "extension/database.svg", - "pot": "extension/powerpoint.svg", - "potm": "extension/powerpoint.svg", - "potx": "extension/powerpoint.svg", - "ppam": "extension/powerpoint.svg", - "pps": "extension/powerpoint.svg", - "ppsm": "extension/powerpoint.svg", - "ppsx": "extension/powerpoint.svg", - "ppt": "extension/powerpoint.svg", - "pptn": "extension/powerpoint.svg", - "pptx": "extension/powerpoint.svg", - "prop": "extension/settings.svg", - "properties": "extension/settings.svg", - "ps1": "extension/powershell.svg", - "ps1xml": "extension/powershell.svg", - "psc1": "extension/powershell.svg", - "psd": "extension/psd.svg", - "psd1": "extension/powershell.svg", - "psm1": "extension/powershell.svg", - "psql": "extension/database.svg", - "pssc": "extension/powershell.svg", - "py": "extension/python.svg", - "py3": "extension/python.svg", - "pyc": "extension/python-misc.svg", - "pylintrc": "extension/python-misc.svg", - "python": "extension/python.svg", - "python-version": "extension/python-misc.svg", - "rar": "extension/zip.svg", - "ruleset": "extension/visualstudio.svg", - "settings": "extension/settings.svg", - "sldm": "extension/powerpoint.svg", - "sldx": "extension/powerpoint.svg", - "sln": "extension/visualstudio.svg", - "sql": "extension/database.svg", - "sqlite": "extension/database.svg", - "sqlite3": "extension/database.svg", - "suo": "extension/visualstudio.svg", - "tgz": "extension/zip.svg", - "ts": "extension/typescript.svg", - "tsbuildinfo": "extension/json.svg", - "tsx": "extension/react_ts.svg", - "txt": "extension/txt.svg", - "vb": "extension/visualstudio.svg", - "vbs": "extension/visualstudio.svg", - "vcxitems": "extension/visualstudio.svg", - "vcxitems.filters": "extension/visualstudio.svg", - "vcxproj": "extension/visualstudio.svg", - "vcxproj.filters": "extension/visualstudio.svg", - "video": "extension/video.svg", - "wbk": "extension/word.svg", - "xhtml": "extension/html.svg", - "xlm": "extension/excel.svg", - "xls": "extension/excel.svg", - "xlsx": "extension/excel.svg", - "xlt": "extension/excel.svg", - "xz": "extension/zip.svg", - "zip": "extension/zip.svg" - }, - "folder": { - ".git": "folder/folder-git.svg", - "android": "folder/folder-android.svg", - "app": "folder/folder-app.svg", - "apps": "folder/folder-app.svg", - "archival": "folder/folder-archive.svg", - "archive": "folder/folder-archive.svg", - "archives": "folder/folder-archive.svg", - "article": "folder/folder-document.svg", - "articles": "folder/folder-document.svg", - "asset": "folder/folder-resource.svg", - "assets": "folder/folder-resource.svg", - "back-ups": "folder/folder-archive.svg", - "backend": "folder/folder-server.svg", - "backup": "folder/folder-archive.svg", - "backups": "folder/folder-archive.svg", - "bin": "folder/folder-dist.svg", - "build": "folder/folder-dist.svg", - "client": "folder/folder-client.svg", - "clients": "folder/folder-client.svg", - "data": "folder/folder-database.svg", - "database": "folder/folder-database.svg", - "databases": "folder/folder-database.svg", - "db": "folder/folder-database.svg", - "desktop": "folder/folder-desktop.svg", - "dist": "folder/folder-dist.svg", - "doc": "folder/folder-document.svg", - "docs": "folder/folder-document.svg", - "document": "folder/folder-document.svg", - "documentation": "folder/folder-document.svg", - "documents": "folder/folder-document.svg", - "downloads": "folder/folder-download.svg", - "font": "folder/folder-font.svg", - "fonts": "folder/folder-font.svg", - "frontend": "folder/folder-client.svg", - "function": "folder/folder-function.svg", - "functions": "folder/folder-function.svg", - "git": "folder/folder-git.svg", - "html": "folder/folder-view.svg", - "ico": "folder/folder-picture.svg", - "icon": "folder/folder-picture.svg", - "icons": "folder/folder-picture.svg", - "image": "folder/folder-picture.svg", - "images": "folder/folder-picture.svg", - "img": "folder/folder-picture.svg", - "javascript": "folder/folder-javascript.svg", - "js": "folder/folder-javascript.svg", - "lib": "folder/folder-lib.svg", - "libraries": "folder/folder-lib.svg", - "library": "folder/folder-lib.svg", - "libs": "folder/folder-lib.svg", - "math": "folder/folder-function.svg", - "music": "folder/folder-music.svg", - "node_modules": "folder/folder-node.svg", - "out": "folder/folder-dist.svg", - "page": "folder/folder-view.svg", - "pages": "folder/folder-view.svg", - "pictures": "folder/folder-picture.svg", - "post": "folder/folder-document.svg", - "posts": "folder/folder-document.svg", - "project": "folder/folder-project.svg", - "projects": "folder/folder-project.svg", - "public": "folder/folder-public.svg", - "publics": "folder/folder-public.svg", - "pwa": "folder/folder-client.svg", - "python": "folder/folder-python.svg", - "release": "folder/folder-dist.svg", - "report": "folder/folder-resource.svg", - "reports": "folder/folder-resource.svg", - "res": "folder/folder-resource.svg", - "resource": "folder/folder-resource.svg", - "resources": "folder/folder-resource.svg", - "screen": "folder/folder-view.svg", - "screens": "folder/folder-view.svg", - "screenshot": "folder/folder-picture.svg", - "screenshots": "folder/folder-picture.svg", - "script": "folder/folder-scripts.svg", - "scripts": "folder/folder-scripts.svg", - "server": "folder/folder-server.svg", - "servers": "folder/folder-server.svg", - "sql": "folder/folder-database.svg", - "src": "folder/folder-resource.svg", - "static": "folder/folder-resource.svg", - "template": "folder/folder-template.svg", - "templates": "folder/folder-template.svg", - "test": "folder/folder-test.svg", - "third-party": "folder/folder-lib.svg", - "vendor": "folder/folder-lib.svg", - "vendors": "folder/folder-lib.svg", - "videos": "folder/folder-video.svg", - "view": "folder/folder-view.svg", - "views": "folder/folder-view.svg", - "website": "folder/folder-public.svg", - "www": "folder/folder-public.svg", - "wwwroot": "folder/folder-public.svg" - }, - "sidebar": { - "desktop": "sidebar/desktop.svg", - "documents": "sidebar/document.svg", - "downloads": "sidebar/download.svg", - "favorites": "sidebar/favorite.svg", - "home": "sidebar/home.svg", - "music": "sidebar/music.svg", - "pictures": "sidebar/picture.svg", - "recent": "sidebar/recent.svg", - "setting": "sidebar/settings.svg", - "trash": "sidebar/trash.svg", - "videos": "sidebar/video.svg" - }, - "contextmenu": { - "layout": "contextmenu/layout.svg", - "sort": "contextmenu/sort.svg", - "refresh": "contextmenu/refresh.svg", - "paste": "contextmenu/paste.svg", - "terminal": "contextmenu/terminal.svg", - "vscode": "contextmenu/vscode.svg", - "pin": "contextmenu/pin.svg", - "new": "contextmenu/new file.svg", - "file property": "contextmenu/file setting.svg", - "folder property": "contextmenu/folder setting.svg", - "open": "contextmenu/open.svg", - "open in new tab": "contextmenu/open in new tab.svg", - "preview": "contextmenu/preview.svg", - "delete": "contextmenu/delete.svg", - "copy": "contextmenu/copy.svg", - "cut": "contextmenu/cut.svg", - "shortcut": "contextmenu/shortcut.svg", - "rename": "contextmenu/rename.svg", - "location": "contextmenu/location.svg", - "undo": "contextmenu/undo.svg", - "redo": "contextmenu/redo.svg" - }, - "settings": { - "about": "settings/about.svg", - "appearance": "settings/appearance.svg", - "preference": "settings/preference.svg" - } -} diff --git a/src/Components/Files/File Operation/open.ts b/src/Components/Files/File Operation/open.ts index be96b5cf..e9abc7d2 100644 --- a/src/Components/Files/File Operation/open.ts +++ b/src/Components/Files/File Operation/open.ts @@ -1,4 +1,4 @@ -import fileIcon from "../File Icon/fileIcon"; +import fileThumbnail from "../../Thumbnail/thumbnail"; import path from "path"; import os from "os"; import Home from '../../Layout/home'; @@ -146,7 +146,7 @@ const displayFiles = async (files: fileData[], dir:string, options?: {reveal: bo } else { await files.forEach(async dirent => { if (IGNORE_FILE.indexOf(dirent.name) !== -1) return; - const preview = await fileIcon(dirent.type === "Image" && dirent.isTrash ? dirent.realPath : path.join(dir, dirent.name), dirent.isDir ? "folder" : "file") + const preview = await fileThumbnail(dirent.type === "Image" && dirent.isTrash ? dirent.realPath : path.join(dir, dirent.name), dirent.isDir ? "folder" : "file") const fileGrid = document.createElement("div") fileGrid.className = "file-grid grid-hover-effect file" if (dirent.isTrash) fileGrid.dataset.isTrash = "true" diff --git a/src/Components/Files/File Preview/preview.ts b/src/Components/Files/File Preview/preview.ts index 6a1b4a1a..74ea43fa 100644 --- a/src/Components/Files/File Preview/preview.ts +++ b/src/Components/Files/File Preview/preview.ts @@ -1,7 +1,7 @@ import { dialog } from '@electron/remote'; import path from 'path'; import { updateTheme } from '../../Theme/theme'; -import FileTypesConfig from '../Types/fileTypes'; +import FileConfig from '../../Config/file.config'; /** * Close the preview file @@ -49,18 +49,16 @@ const Preview = (filePath: string): void => { const ext = filePath.split('.').pop().toLowerCase(); const basename = path.basename(filePath); let previewed = false; - for (const type of FileTypesConfig()) { + for (const type of FileConfig()) { if ( - (type.fileNames?.indexOf(basename) !== undefined && - type.fileNames?.indexOf(basename) !== -1) || - (type.folderNames?.indexOf(basename) !== undefined && - type.folderNames?.indexOf(basename) !== -1) + type.fileNames?.indexOf(basename) !== undefined && + type.fileNames?.indexOf(basename) !== -1 ) { type?.preview?.(filePath, (html) => changePreview(html)); previewed = true; } } - for (const type of FileTypesConfig()) { + for (const type of FileConfig()) { if ( type.extension?.indexOf(ext) !== undefined && type.extension?.indexOf(ext) !== -1 diff --git a/src/Components/Files/File Type/type.ts b/src/Components/Files/File Type/type.ts index bd49e93b..e6f9a0bd 100644 --- a/src/Components/Files/File Type/type.ts +++ b/src/Components/Files/File Type/type.ts @@ -1,5 +1,6 @@ import path from 'path'; -import FileTypesConfig from '../Types/fileTypes'; +import folderConfig from '../../Config/folder.config'; +import FileConfig from '../../Config/file.config'; /** * Get type of a file name * @param {string} filename - File name @@ -10,17 +11,23 @@ const getType = (filename: string, isDir?: boolean): string => { filename = path.basename(filename); const ext = filename.split('.').pop().toLowerCase(); // Prioritize exact file name and folder name over file extension - for (const type of FileTypesConfig()) { + for (const type of FileConfig()) { if ( - (type.fileNames?.indexOf(filename) !== undefined && - type.fileNames?.indexOf(filename) !== -1) || - (type.folderNames?.indexOf(filename) !== undefined && - type.folderNames?.indexOf(filename) !== -1) + type.fileNames?.indexOf(filename) !== undefined && + type.fileNames?.indexOf(filename) !== -1 ) { return type.type; } } - for (const type of FileTypesConfig()) { + for (const type of folderConfig()) { + if ( + type.folderNames?.indexOf(filename) !== undefined && + type.folderNames?.indexOf(filename) !== -1 + ) { + return type.type; + } + } + for (const type of FileConfig()) { if ( type.extension?.indexOf(ext) !== undefined && type.extension?.indexOf(ext) !== -1 diff --git a/src/Components/Functions/extractExeIcon.ts b/src/Components/Functions/extractExeIcon.ts new file mode 100644 index 00000000..276f0c2a --- /dev/null +++ b/src/Components/Functions/extractExeIcon.ts @@ -0,0 +1,33 @@ +const extractExeIcon = (filePath: string): string => { + const fs = require('fs'); + const path = require('path'); + const { extractIcon } = require('../../Lib/extracticon/bindings'); + const electron = require('electron'); + const basename = + filePath.split(/[\\/]/)[filePath.split(/[\\/]/).length - 1]; + const app = + electron.app || (electron.remote && electron.remote.app) || null; + const EXE_ICON_CACHE_DIR = path.join( + app.getPath('userData'), + 'Cache/Exe Icon' + ); + + // Create cache directory if not exist + if (!fs.existsSync(EXE_ICON_CACHE_DIR)) { + if (!fs.existsSync(path.join(EXE_ICON_CACHE_DIR, '..'))) { + fs.mkdirSync(path.join(EXE_ICON_CACHE_DIR, '..')); + } + fs.mkdirSync(EXE_ICON_CACHE_DIR); + } + const ICON_FILE_NAME = path.join(EXE_ICON_CACHE_DIR, basename + '.ico'); + + // Cache the icon parsed from the exe + if (fs.existsSync(ICON_FILE_NAME)) { + return ICON_FILE_NAME; + } else { + const buffer = extractIcon(filePath, 'large'); + fs.writeFileSync(ICON_FILE_NAME, buffer); + return ICON_FILE_NAME; + } +}; +export default extractExeIcon; diff --git a/src/Components/Layout/home.ts b/src/Components/Layout/home.ts index e58b9024..e0bcad8c 100644 --- a/src/Components/Layout/home.ts +++ b/src/Components/Layout/home.ts @@ -12,7 +12,7 @@ import { updateTheme } from '../Theme/theme'; import fs from 'fs'; import Translate from '../I18n/i18n'; import nativeDrag from '../Files/File Operation/drag'; -import fileIcon from '../Files/File Icon/fileIcon'; +import fileThumbnail from '../Thumbnail/thumbnail'; import { startLoading, stopLoading } from '../Functions/Loading/loading'; import storage from 'electron-json-storage-sync'; import LAZY_LOAD from '../Functions/lazyLoadingImage'; @@ -92,7 +92,7 @@ const homeFiles = (callback: cb) => { files = files.sort((a, b) => -(Number(a.isDir) - Number(b.isDir))); } await files.forEach(async (file) => { - const preview = await fileIcon( + const preview = await fileThumbnail( path.join(os.homedir(), file.name), file.isDir ? 'folder' : 'file' ); diff --git a/src/Components/Layout/hover.ts b/src/Components/Layout/hover.ts index ca58133e..57e18474 100644 --- a/src/Components/Layout/hover.ts +++ b/src/Components/Layout/hover.ts @@ -1,5 +1,5 @@ import path from 'path'; -import { IMAGE_TYPES } from '../Files/Types/fileTypes'; +import { IMAGE_TYPES } from '../Config/file.config'; /** * Listen to mouse hovering diff --git a/src/Components/Layout/sidebar.ts b/src/Components/Layout/sidebar.ts index bc3776b2..beae6e7f 100644 --- a/src/Components/Layout/sidebar.ts +++ b/src/Components/Layout/sidebar.ts @@ -1,6 +1,6 @@ import fs from 'fs'; import storage from 'electron-json-storage-sync'; -import fileIcon from '../Files/File Icon/fileIcon'; +import fileThumbnail from '../Thumbnail/thumbnail'; import { getDrives } from '../Drives/drives'; import getDriveBasePath from '../Functions/basePath'; import { updateTheme } from '../Theme/theme'; @@ -49,7 +49,7 @@ const createSidebar = (): void => { } favoritesElement += `