Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c769729
FsZip: added new Zip filesystem
warpdesign Feb 21, 2023
9b7a39a
Fs: added options.readonly
warpdesign Feb 22, 2023
c67c887
Fs: added subPath parameter to getNewFs
warpdesign Feb 22, 2023
c2c9994
FsZip: fixed open directory inside zip file
warpdesign Feb 24, 2023
3d2c58a
FsZip: fixed crash when opening a non-existing dir inside zip
warpdesign Feb 24, 2023
e2536ad
FsZip: fixed reading entries when dir is not listed
warpdesign Feb 27, 2023
8231338
Fs: prevent rename/delete/paste/drop on a readonly filesystem
warpdesign Feb 28, 2023
162d818
FsZip: close zip when exiting Fs
warpdesign Feb 28, 2023
4310d49
FsZip: implement getStream
warpdesign Feb 28, 2023
08ec18b
FsZip: fixed directory entry path in sub directory
warpdesign Feb 28, 2023
52d37f4
Tools: added zlib polyfill for e2e builds
warpdesign Feb 28, 2023
8b08fe3
Tools: fix process mock typo
warpdesign Feb 28, 2023
44b1c5e
Tests: fixed tests that were broken after adding zip support
warpdesign Feb 28, 2023
e9b2cc2
FsZip: instanciate a new StreamZip from cd() if zipPath has changed
warpdesign Mar 3, 2023
0f54d9f
FsZip: show EACCES error if read access is denied
warpdesign Mar 3, 2023
de1fce3
FsZip: reopen the zip if needed
warpdesign Mar 7, 2023
c0ca48b
Toolbar: disable spellcheck on path input
warpdesign Mar 7, 2023
439185d
FileCache: do not attempt to open a terminal if fs.options is indirect
warpdesign Mar 7, 2023
7bfe5aa
FsZip: do not attempt to open folder 'foo.zip' as an archive
warpdesign Mar 9, 2023
166bf17
FileContextMenu: disable paste/delete when cache is readonly
warpdesign Mar 9, 2023
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
2 changes: 1 addition & 1 deletion e2e/cypress/mocks/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module.exports = {
process: 'React-Explorer',
platform: __PLATFORM__,
version: __NODE__,
verisons: {
versions: {
chrome: 'cypress chrome',
electron: 'cypress electron',
},
Expand Down
1 change: 1 addition & 0 deletions e2e/webpack.config.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const baseConfig = {
assert: require.resolve('assert/'),
util: require.resolve('util/'),
path: require.resolve('path-browserify'),
zlib: require.resolve('browserify-zlib'),
},
},
module: {
Expand Down
41 changes: 39 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"aws-sdk": "^2.514.0",
"browserify-zlib": "^0.2.0",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.1",
Expand Down Expand Up @@ -132,6 +133,7 @@
"mobx": "^6.6.2",
"mobx-react": "^7.5.3",
"node-disk-info": "git+https://[email protected]/warpdesign/node-disk-info.git",
"node-stream-zip": "^1.15.0",
"react": "^16.9.0",
"react-dnd": "^14.0.5",
"react-dnd-html5-backend": "^14.1.0",
Expand Down
6 changes: 3 additions & 3 deletions src-experimental/services/plugins/FsSimpleFtp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as fs from 'fs'
import { Transform, Readable, Writable } from 'stream'
import { EventEmitter } from 'events'
import * as nodePath from 'path'
import { isWin } from '../../utils/platform'
import { isWin } from '$src/utils/platform'

function serverPart(str: string, lowerCase = true): string {
const info = new URL(str)
Expand Down Expand Up @@ -192,7 +192,7 @@ class Client {
}

@canTimeout
getStream(path: string, writeStream: Writable): Promise<Readable> {
getStream(path: string, writeStream: Writable): Promise<NodeJS.ReadableStream> {
this.ftpClient.download(writeStream, path)
return Promise.resolve(this.ftpClient.ftp.dataSocket)
}
Expand Down Expand Up @@ -418,7 +418,7 @@ class SimpleFtpApi implements FsApi {
}
}

async getStream(path: string, file: string, transferId = -1): Promise<Readable> {
async getStream(path: string, file: string, transferId = -1): Promise<NodeJS.ReadableStream> {
try {
// create a duplex stream
const transform = new Transform({
Expand Down
8 changes: 2 additions & 6 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,18 @@ const App = observer(() => {
status: activeCache.status,
path: activeCache.path,
selectedLength: activeCache.selected.length,
// enable when FsZip is merged
isReadonly: false,
isIndirect: false,
historyLength: activeCache.history.length,
historyCurrent: activeCache.current,
isRoot: API.isRoot(activeCache.path),
// isReadonly: activeCache.getFS().options.readonly,
// isIndirect: activeCache.getFS().options.indirect,
isReadonly: fs.options.readonly,
isIndirect: fs.options.indirect,
isOverlayOpen: refIsOverlayOpen.current,
activeViewTabNums: activeView.caches.length,
isExplorer: appState.isExplorer,
language: settingsState.lang,
filesLength: activeCache.files.length,
clipboardLength: appState.clipboard.files.length,
activeViewId: activeView.viewId,
// missing: about opened, tab: is it needed?
}
}, [appState])

Expand Down
23 changes: 14 additions & 9 deletions src/components/FileView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ const FileView = observer(({ hide }: Props) => {
}

const onInlineEdit = ({ action, data }: InlineEditEvent) => {
if (cache.getFS().options.readonly) {
return
}

switch (action) {
case 'validate':
appState.renameEditingFile(cache, data as string)
Expand All @@ -183,15 +187,16 @@ const FileView = observer(({ hide }: Props) => {
}

const openFileOrDirectory = (file: FileDescriptor, useInactiveCache: boolean): void => {
if (!file.isDir) {
cache.openFile(appState, file)
} else {
const dir = {
dir: cache.join(file.dir, file.fullname),
fullname: '',
}
appState.openDirectory(dir, !useInactiveCache)
}
// if (!file.isDir) {
// cache.openFile(appState, file)
// } else {
// // const dir = {
// // dir: cache.join(file.dir, file.fullname),
// // fullname: '',
// // }
// appState.openDirectory(file, !useInactiveCache)
// }
appState.openFileOrDirectory(file, useInactiveCache)
}

const onOpenFile = (e: KeyboardEvent): void => {
Expand Down
7 changes: 6 additions & 1 deletion src/components/SideView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ export const SideView = observer(({ hide, viewState }: SideViewProps) => {
canDrop: ({ fileState }: DraggedObject) => {
const sourceViewId = fileState.viewId
const fileCache = viewState.getVisibleCache()
return viewState.viewId !== sourceViewId && fileCache.status !== 'busy' && !fileCache.error
return (
viewState.viewId !== sourceViewId &&
fileCache.status !== 'busy' &&
!fileCache.error &&
!fileCache.getFS().options.readonly
)
},
drop: (item) => appState.drop(item, viewState.getVisibleCache()),
collect: (monitor) => ({
Expand Down
16 changes: 15 additions & 1 deletion src/components/Statusbar.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as React from 'react'
import { Button, Intent } from '@blueprintjs/core'
import { Button, Colors, Icon, Intent } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import { Tooltip2 } from '@blueprintjs/popover2'
import { observer } from 'mobx-react'
import { useTranslation } from 'react-i18next'

import { useStores } from '$src/hooks/useStores'
import { filterDirs, filterFiles } from '$src/utils/fileUtils'

Expand Down Expand Up @@ -33,6 +34,7 @@ const Statusbar = observer(() => {
const { t } = useTranslation()
const fileCache = viewState.getVisibleCache()
const { files, showHiddenFiles } = fileCache
const showReadOnlyIcon = fileCache.getFS().options.readonly

const numDirs = filterDirs(files).length
const numFiles = filterFiles(files).length
Expand All @@ -48,6 +50,18 @@ const Statusbar = observer(() => {
{`${t('STATUS.FILES', { count: numFiles })}, ${t('STATUS.FOLDERS', {
count: numDirs,
})}`}
{showReadOnlyIcon && (
<div
style={{
flexGrow: 1,
display: 'flex',
justifyContent: 'end',
marginRight: '0.5rem',
}}
>
<Icon icon={IconNames.LOCK} color={Colors.GRAY3} title={t('FS.READONLY')} />
</div>
)}
</div>
)
})
Expand Down
6 changes: 3 additions & 3 deletions src/components/Toolbar/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ describe('Toolbar', () => {
await user.paste('/foo')
await user.keyboard('{Enter}')

expect(cache.cd).toHaveBeenCalledWith('/foo')
expect(cache.cd).toHaveBeenCalledWith('/foo', '')

expect(input).not.toHaveFocus()
})
Expand All @@ -249,7 +249,7 @@ describe('Toolbar', () => {
await user.paste('/foo')
await user.click(container.querySelector('[data-icon="arrow-right"]'))

expect(cache.cd).toHaveBeenCalledWith('/foo')
expect(cache.cd).toHaveBeenCalledWith('/foo', '')
})

it('should show an alert then restore previous value when the user clicked on the submit button and the cache failed to change directory', async () => {
Expand All @@ -269,7 +269,7 @@ describe('Toolbar', () => {
await user.paste('/foo')
await user.click(container.querySelector('[data-icon="arrow-right"]'))

expect(cache.cd).toHaveBeenCalledWith('/foo')
expect(cache.cd).toHaveBeenCalledWith('/foo', '')

await screen.findByText(`${error.message} (${error.code})`)

Expand Down
14 changes: 10 additions & 4 deletions src/components/Toolbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef, useCallback } from 'react'
import { observer } from 'mobx-react'
import { InputGroup, ControlGroup, Button, ButtonGroup, Intent, HotkeysTarget2, Classes } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import { InputGroup, ControlGroup, Button, ButtonGroup, Intent, HotkeysTarget2, Classes, Icon } from '@blueprintjs/core'
import { IconName, IconNames } from '@blueprintjs/icons'
import { Popover2 } from '@blueprintjs/popover2'
import { useTranslation } from 'react-i18next'

Expand Down Expand Up @@ -58,7 +58,11 @@ export const Toolbar = observer(({ active }: Props) => {
const onSubmit = async (shouldSelectTextOnError = false): Promise<void> => {
if (cache.path !== path) {
try {
await cache.cd(path)
// await cache.cd(path)
await appState.openDirectory({
dir: path,
fullname: '',
})
inputRef.current.blur()
} catch (e) {
const err = e as LocalizedError
Expand Down Expand Up @@ -222,7 +226,7 @@ export const Toolbar = observer(({ active }: Props) => {
<Popover2
content={
<FileMenu
isDisabled={!cache || cache.error}
isDisabled={!cache || cache.error || cache.getFS().options.readonly}
selectedItemsLength={selected.length}
onFileAction={onFileAction}
/>
Expand All @@ -237,6 +241,7 @@ export const Toolbar = observer(({ active }: Props) => {
onChange={onPathChange}
onKeyUp={onKeyUp}
placeholder={t('COMMON.PATH_PLACEHOLDER')}
leftElement={<Icon icon={cache.getFS().icon as IconName} style={{ opacity: 0.3 }} />}
rightElement={reloadButton}
value={path}
inputRef={inputRef}
Expand All @@ -246,6 +251,7 @@ export const Toolbar = observer(({ active }: Props) => {
// allows input shrinking to a very low width:
// without it, it would refuse shrinking below 100px
size={1}
spellCheck={false}
/>
{isMakedirDialogOpen && (
<MakedirDialog
Expand Down
4 changes: 2 additions & 2 deletions src/components/__tests__/FileView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ describe('FileView', () => {

expect(appState.openDirectory).toHaveBeenCalledWith(
expect.objectContaining({
dir: cache.join(file.dir, file.fullname),
fullname: '',
dir: file.dir,
fullname: file.fullname,
}),
true,
)
Expand Down
4 changes: 2 additions & 2 deletions src/components/dialogs/PrefsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ const PrefsDialog = observer(({ isOpen, onClose }: PrefsProps) => {

// TODO: we could have a default folder that's not using FsLocal
const [isFolderValid, setIsFolderValid] = useState(
() => FsLocal.canread(defaultFolder) && FolderExists(defaultFolder),
() => FsLocal.canread(defaultFolder, '') && FolderExists(defaultFolder),
)

const checkPath: (path: string) => void = debounce((path: string) => {
const isValid = FsLocal.canread(path) && FolderExists(path)
const isValid = FsLocal.canread(path, '') && FolderExists(path)

if (path !== settingsState.defaultFolder) {
setIsFolderValid(isValid)
Expand Down
9 changes: 6 additions & 3 deletions src/components/menus/FileContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ const FileContextMenu = ({ fileUnderMouse }: Props) => {
const { appState } = useStores('appState')
const clipboard = appState.clipboard
const cache = appState.getActiveCache()
const isReadonly = cache.options.readonly

// TODO: disable delete/paste when cahce.fs.readonly is true
const numFilesInClipboard = clipboard.files.length
const isInSelection = fileUnderMouse && !!cache.selected.find((file) => sameID(file.id, fileUnderMouse.id))
const isPasteEnabled = numFilesInClipboard && ((!fileUnderMouse && !cache.error) || fileUnderMouse?.isDir)
// FIXME: if fileUnderMouse is a folder, we could paste inside that folder. Right now paste doesn't care about
// where click happens: it just uses current cache as target.
// ((!fileUnderMouse && !cache.error) || fileUnderMouse?.isDir)
const isPasteEnabled = numFilesInClipboard && !isReadonly

const onCopy = () => {
clipboard.setClipboard(cache, !isInSelection ? [fileUnderMouse] : undefined)
Expand Down Expand Up @@ -59,7 +62,7 @@ const FileContextMenu = ({ fileUnderMouse }: Props) => {
icon="delete"
intent={Intent.DANGER}
text={t('APP_MENUS.DELETE')}
disabled={!fileUnderMouse}
disabled={!fileUnderMouse || isReadonly}
onClick={onDelete}
/>
</Menu>
Expand Down
Loading