From 733776d0bb86a36e74168cdc1227ca360fe2f1f6 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 16 Jun 2021 15:28:46 +0200 Subject: [PATCH 01/15] Handle all files, open not zips with default --- package.json | 4 +- src/components/browser/Browser.vue | 12 ++--- .../components/{DriveList.vue => Sidebar.vue} | 0 src/electron/background.js | 4 +- src/electron/service/browser.js | 52 +++++++++++++------ src/electron/utilities/service.js | 10 +++- src/store/browser.js | 21 ++++---- vue.config.js | 13 +++-- 8 files changed, 73 insertions(+), 43 deletions(-) rename src/components/browser/components/{DriveList.vue => Sidebar.vue} (100%) diff --git a/package.json b/package.json index 0b4921b..6427984 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { - "name": "vue3-empty", + "name": "zippy", "version": "0.1.0", "private": true, + "author": "Roman Ranniew", + "description": "Modern archives manager", "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", diff --git a/src/components/browser/Browser.vue b/src/components/browser/Browser.vue index b16d174..cdfb1d7 100644 --- a/src/components/browser/Browser.vue +++ b/src/components/browser/Browser.vue @@ -1,6 +1,6 @@ @@ -81,7 +81,7 @@ export default { background: fade(black, 10%); } } -.browser__drivelist { +.browser__sidebar { display: inline-block; vertical-align: top; width: 150px; diff --git a/src/components/browser/components/DriveList.vue b/src/components/browser/components/Sidebar.vue similarity index 100% rename from src/components/browser/components/DriveList.vue rename to src/components/browser/components/Sidebar.vue diff --git a/src/electron/background.js b/src/electron/background.js index ec021b3..1cfd671 100644 --- a/src/electron/background.js +++ b/src/electron/background.js @@ -16,8 +16,8 @@ const unzipPath = path.parse(process.argv[1]) if (unzipPath.base === 'dist_electron' || unzipPath.base === 'zippy') { async function createWindow() { const win = new BrowserWindow({ - width: 800, - height: 600, + width: 1600, + height: 1000, webPreferences: { nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION, diff --git a/src/electron/service/browser.js b/src/electron/service/browser.js index 0a5f6ec..7884fc8 100644 --- a/src/electron/service/browser.js +++ b/src/electron/service/browser.js @@ -1,10 +1,11 @@ +const { CLIEngine } = require('eslint') const { saveLog } = require('../utilities/log') function getDirPattern(isInsideArchive, filterZip = true) { let pattern = '*' if (!isInsideArchive) { pattern = '*(!(*.*)' if (filterZip) { - 'rar, zip, 7z'.split(', ').forEach(ext => (pattern += `|*.${ext}`)) + 'rar, zip, 7z'.split(', ').forEach((ext) => (pattern += `|*.${ext}`)) } else { pattern += '|*.*' } @@ -33,7 +34,7 @@ function getDirectoryFiles(dir, filterZipFiles, openedArchive) { return new Promise((resolve, reject) => { glob(getDirPattern(openedArchive, filterZipFiles), { cwd: dir }, async (error, newFiles) => { - files = await Promise.all(newFiles.map(async file => await getFileStats(file, dir))) + files = await Promise.all(newFiles.map(async (file) => await getFileStats(file, dir))) if (error) { reject(error) } @@ -41,9 +42,31 @@ function getDirectoryFiles(dir, filterZipFiles, openedArchive) { }) }) } +async function handleFile(fileDir, filterZipFiles) { + const path = require('path') + const { handledExtensions } = require('../utilities/service') + + const fileExtension = path.extname(fileDir) + if (Object.values(handledExtensions).includes(fileExtension)) { + try { + const archiveDir = await openFile(fileDir) + const targetDirParsed = path.parse(fileDir) + return await readDir(archiveDir, filterZipFiles, `${targetDirParsed.name}${targetDirParsed.ext}`) + } catch (error) { + saveLog('ERROR', 'open-file', error) + return error + } + } else { + const shell = require('electron').shell + + shell.openPath(fileDir) + return { + handledDefault: true, + } + } +} async function readDir(targetDir, filterZipFiles, openedArchive) { - const path = require('path') const fs = require('fs-extra') if (openedArchive) { if (targetDir.search(openedArchive) < 0) { @@ -51,29 +74,24 @@ async function readDir(targetDir, filterZipFiles, openedArchive) { } } // --If target directory is a file - const stat = await fs.stat(targetDir) - const isFile = stat.isFile() + const targetStat = await fs.stat(targetDir) + const isFile = targetStat.isFile() if (isFile) { - try { - const archiveDir = await openFile(targetDir) - const targetDirParsed = path.parse(targetDir) - return await readDir(archiveDir, filterZipFiles, `${targetDirParsed.name}${targetDirParsed.ext}`) - } catch (error) { - saveLog('ERROR', 'open-file', error) - } + return await handleFile(targetDir, filterZipFiles) } else { // -- - let files + let files, error try { files = await getDirectoryFiles(targetDir, filterZipFiles, openedArchive) - } catch (error) { - saveLog('ERROR', 'glob-read', error) + } catch (dirError) { + error = dirError + saveLog('ERROR', 'glob-read', dirError) } - return { files, targetDir } + return { files, targetDir, error } } } module.exports = { - readDir + readDir, } diff --git a/src/electron/utilities/service.js b/src/electron/utilities/service.js index 9b30343..2781aea 100644 --- a/src/electron/utilities/service.js +++ b/src/electron/utilities/service.js @@ -1,10 +1,16 @@ +const handledExtensions = { + ZIP: '.zip', + SEVENZIP: '.7z', +} + module.exports = { + handledExtensions, extractArchive(sourcePath, targetDir) { const path = require('path') return new Promise((resolve, reject) => { const extensions = { - '.zip': () => { + [handledExtensions.ZIP]: () => { const extract = require('extract-zip') extract(sourcePath, { dir: targetDir }, (error) => { @@ -15,7 +21,7 @@ module.exports = { resolve(targetDir) }) }, - '.7z': () => { + [handledExtensions.SEVENZIP]: () => { const _7z = require('7zip-min') _7z.unpack(sourcePath, targetDir, (error) => { if (error) { diff --git a/src/store/browser.js b/src/store/browser.js index 5728edd..f096892 100644 --- a/src/store/browser.js +++ b/src/store/browser.js @@ -9,14 +9,14 @@ const initialState = { filterZipFiles: false, currentDir: '', previousDir: null, - previousDirExists: false + previousDirExists: false, } export default { state: initialState, namespaced: true, mutations: { - ...makeMutations(initialState) + ...makeMutations(initialState), }, actions: { getDrives() { @@ -24,12 +24,12 @@ export default { win32: async () => { // const drives = await getWinDrives() // context.commit('drives', drives) - } + }, } platforms[process.platform]?.() }, - readDir: async function(context, dir) { + readDir: async function (context, dir) { const path = require('path') const os = require('os') if (!dir) { @@ -38,16 +38,17 @@ export default { context.commit('previousDir', context.state.currentDir) context.commit('loading', true) const response = await window.api.readDir({ dir, filterZipFiles: context.state.filterZipFiles }) - window.title = response.targetDir - context.commit('files', response.files) - context.commit('previousDirExists', path.dirname(dir) !== dir) - context.commit('currentDir', dir) + if (!response.handledDefault) { + context.commit('files', response.files) + context.commit('previousDirExists', path.dirname(dir) !== dir) + context.commit('currentDir', dir) + } context.commit('loading', false) }, sortFiles(context, payload) { const { orderBy } = require('lodash') context.commit('files', orderBy(context.rootState.browser.files, payload)) - } - } + }, + }, } diff --git a/vue.config.js b/vue.config.js index 531896a..fc5ea92 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,15 +1,18 @@ +const glob = require('glob') +const electronFiles = 'service/browser.js, service/ipc.js, utilities/file/utilities/service.js' module.exports = { pluginOptions: { electronBuilder: { preload: 'src/electron/preload.js', + mainProcessWatch: electronFiles.split(', ').map((file) => `src/electron/${file}`), mainProcessFile: 'src/electron/background.js', builderOptions: { appId: 'com.zippy.app', productName: 'zippy', win: { - icon: './public/zippy.ico' - } - } - } - } + icon: './public/zippy.ico', + }, + }, + }, + }, } From 7a58a9f61ac07495142fa7813d3ecf8992920fa2 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 21 Jun 2021 17:14:07 +0200 Subject: [PATCH 02/15] Handle key events --- .eslintrc.js | 9 +- src/components/browser/Browser.vue | 88 ++++++++++++++++++- src/components/browser/components/File.vue | 22 ++--- .../browser/components/FilterBar.vue | 2 +- src/electron/service/browser.js | 10 +-- src/electron/utilities/file.js | 14 ++- 6 files changed, 114 insertions(+), 31 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4d77354..d74d7c5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,16 +1,17 @@ module.exports = { root: true, env: { - node: true + node: true, }, extends: ['plugin:vue/vue3-essential', 'eslint:recommended'], parserOptions: { - parser: 'babel-eslint' + parser: 'babel-eslint', }, rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-unused-vars': process.env.NODE_ENV === 'production' ? 'warn' : 'off', - 'no-undef': process.env.NODE_ENV === 'production' ? 'warn' : 'off' - } + 'no-undef': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'array-element-newline': ['error', 'consistent'], + }, } diff --git a/src/components/browser/Browser.vue b/src/components/browser/Browser.vue index cdfb1d7..45e34b2 100644 --- a/src/components/browser/Browser.vue +++ b/src/components/browser/Browser.vue @@ -8,7 +8,15 @@
../
    - + +
@@ -39,12 +47,22 @@ import Sidebar from './components/Sidebar.vue' import FilterBar from './components/FilterBar.vue' import Loader from './components/Loader.vue' import File from './components/File.vue' -import { computed } from 'vue' +import { computed, onUnmounted, ref } from 'vue' import { useStore } from 'vuex' + +const keys = { + ctrl: 'Control', + shift: 'Shift', + ctrlShift: 'ControlShift', +} + export default { components: { Loader, Sidebar, FilterBar, File }, + setup() { const store = useStore() + let selectedFiles = ref([]) + const heldKey = ref(null) const loadingDrives = computed(() => store.state.browser.loadingDrives) const loading = computed(() => store.state.browser.loading) @@ -52,10 +70,41 @@ export default { const previousDir = computed(() => store.state.browser.previousDir) const currentDir = computed(() => store.state.browser.currentDir) const files = computed(() => store.state.browser.files) + const filesPaths = computed(() => files.value.map((file) => file.fullPath)) + const selectedFilesPaths = computed(() => selectedFiles.value.map((file) => file.fullPath)) const previousDirExists = computed(() => store.state.browser.previousDirExists) + function handleKeyEvent(ev) { + if (ev.key !== keys.shift && ev.key !== keys.ctrl) { + return + } + if (ev.key === keys.shift && ev.ctrlKey) { + heldKey.value = ev.type === 'keydown' ? keys.ctrlShift : null + return + } + + heldKey.value = ev.type === 'keydown' ? ev.key : null + } + + function getSelectedFiles(path) { + let firstSelectedFileIndex = filesPaths.value.indexOf(selectedFilesPaths.value[0]) + let secondSelectedFileIndex = filesPaths.value.indexOf(path) + if (firstSelectedFileIndex > secondSelectedFileIndex) { + // prettier-ignore + [firstSelectedFileIndex, secondSelectedFileIndex] = [secondSelectedFileIndex, firstSelectedFileIndex] + } + return files.value.slice(firstSelectedFileIndex, secondSelectedFileIndex + 1) + } + store.dispatch('browser/getDrives') store.dispatch('browser/readDir') + window.addEventListener('keydown', handleKeyEvent) + window.addEventListener('keyup', handleKeyEvent) + onUnmounted(() => { + window.removeEventListener('keydown', handleKeyEvent) + window.removeEventListener('keyup', handleKeyEvent) + }) + return { loadingDrives, loading, @@ -63,7 +112,42 @@ export default { previousDir, currentDir, files, + filesPaths, + selectedFiles, + selectedFilesPaths, previousDirExists, + checkIfSelected(path) { + return selectedFilesPaths.value.includes(path) + }, + select(event, path) { + const actions = { + [keys.ctrl](path) { + const { pullAt, cloneDeep } = require('lodash') + const fileIndex = filesPaths.value.indexOf(path) + const selectedFilePathIndex = selectedFilesPaths.value.indexOf(path) + const isSelected = selectedFilePathIndex >= 0 + if (isSelected) { + const selectedFilesCopy = cloneDeep(selectedFiles.value) + // console.log(...filesCopy, 'selected', path, selectedFilePathIndex) + pullAt(selectedFilesCopy, selectedFilePathIndex) + selectedFiles.value = selectedFilesCopy + } else { + selectedFiles.value = [...selectedFiles.value, files.value[fileIndex]] + } + }, + [keys.shift](path) { + selectedFiles.value = getSelectedFiles(path) + }, + [keys.ctrlShift](path) { + selectedFiles.value = [...selectedFiles.value, ...getSelectedFiles(path)] + }, + default(path) { + const fileIndex = filesPaths.value.indexOf(path) + selectedFiles.value = [files.value[fileIndex]] + }, + } + actions[heldKey.value] ? actions[heldKey.value](path) : actions.default(path) + }, readDir(path) { store.dispatch('browser/readDir', path) }, diff --git a/src/components/browser/components/File.vue b/src/components/browser/components/File.vue index 38113f7..9d9be1d 100644 --- a/src/components/browser/components/File.vue +++ b/src/components/browser/components/File.vue @@ -1,13 +1,13 @@