diff --git a/app/api.js b/app/api.js index d3ac1d4..7d88082 100644 --- a/app/api.js +++ b/app/api.js @@ -98,10 +98,10 @@ export function wrapFiles (links) { /** * This function will allow the user to add a file or multiple files to the IPFS repo. * Accepts a string or a string array. - * Wraps the files in a directory. + * Wraps the files in a directory (if it's not disabled in the settings). * * ``` - * Wrapper { + * Result { * hash: string; * path: string; * size: number; @@ -109,9 +109,10 @@ export function wrapFiles (links) { * ``` * * @param {string[]} filePaths - * @returns {Promise} promise resolving with the wrapper + * @param {boolean} forceWrapping - if true it will wrap the files regardless of the user settings + * @returns {Promise} promise resolving with the wrapper or the file */ -export function addFilesFromFSPath (filePaths, _queryGateways = queryGateways) { +export function addFilesFromFSPath (filePaths, forceWrapping = false, _queryGateways = queryGateways) { if (!IPFS_CLIENT) return Promise.reject(ERROR_IPFS_UNAVAILABLE) trackEvent('addFilesFromFSPath', { count: filePaths.length }) @@ -173,7 +174,6 @@ export function addFilesFromFSPath (filePaths, _queryGateways = queryGateways) { // IPFS_CLIENT.util.addFromFs always returns an array // (because it can upload an dir recursively), // which is why we expect an array of arrays - const rootFiles = fileUploadResults.map(result => { /** * If it was a directory it will be last @@ -192,29 +192,40 @@ export function addFilesFromFSPath (filePaths, _queryGateways = queryGateways) { return result[result.length - 1] }) - return wrapFiles(rootFiles) - .then(wrapper => Promise.all([ - // This value is needed further in the chain - wrapper, - // Pin the wrapper directory - IPFS_CLIENT.pin.add(wrapper.hash), - // Unpin the initial uploads - ...rootFiles.map(rootFile => IPFS_CLIENT.pin.rm(rootFile.hash)) - ])) + return Promise.resolve(forceWrapping ? false : Settings.get('disableWrapping')) + .then(disableWrapping => { + /** + * Skip wrapping the files if the user disable this feature + */ + if (disableWrapping) return Promise.resolve() + + return wrapFiles(rootFiles) + .then(wrapper => Promise.all([ + // This value is needed further in the chain + wrapper, + // Pin the wrapper directory + IPFS_CLIENT.pin.add(wrapper.hash), + // Unpin the initial uploads + ...rootFiles.map(rootFile => IPFS_CLIENT.pin.rm(rootFile.hash)) + ])) + }) /** - * Query the gateways and return the wrapper dir + * Query the gateways and return the wrapper dir or the rootFiles */ .then(results => { - const wrapper = results[0] + const wrapper = results ? results[0] : null if (!Settings.get('skipGatewayQuery')) { // Query all the uploaded files fileUploadResults.forEach(files => files.forEach(file => _queryGateways(file.hash))) - // Query the wrapper - _queryGateways(wrapper.hash) + // Query the wrapper if it exists + if (wrapper) { + _queryGateways(wrapper.hash) + } } - return Promise.resolve(wrapper) + // Return the wrapper if it exists, otherwise the rootFiles last item + return Promise.resolve(wrapper || rootFiles[rootFiles.length - 1]) }) }) .catch(reportAndReject) diff --git a/app/api.test.js b/app/api.test.js index 89b7229..b4289c3 100644 --- a/app/api.test.js +++ b/app/api.test.js @@ -37,8 +37,13 @@ jest.mock('request-promise-native', () => { jest.mock('electron-settings', () => { const getMock = jest.fn() + // get('disableWrapping') + .mockReturnValueOnce(false) // get('skipGatewayQuery') .mockReturnValueOnce(true) + // get('disableWrapping') + .mockReturnValueOnce(true) + // get('skipGatewayQuery') .mockReturnValueOnce(false) return { get: getMock @@ -272,7 +277,7 @@ describe('api.js', () => { }) const queryGatewaysMock = jest.fn() // act - return api.addFilesFromFSPath(['./textfiles'], queryGatewaysMock) + return api.addFilesFromFSPath(['./textfiles'], false, queryGatewaysMock) .then(result => { // assert expect(addFromFsMock).toHaveBeenCalledWith('./textfiles', { @@ -298,7 +303,7 @@ describe('api.js', () => { }) }) - it('should add the file/dir recursively and query the gateways', () => { + it('should add the file/dir recursively without wrapper and query the gateways', () => { // arrange // arrange const addFromFsMock = jest.fn() @@ -315,17 +320,6 @@ describe('api.js', () => { ])) const objectPutMock = jest.fn() - .mockReturnValue(Promise.resolve({ - toJSON: () => { - return { - multihash: 'QmRgutAxd8t7oGkSm4wmeuByG6M51wcTso6cubDdQtu003', - size: 60 - } - } - })) - - const pinAddMock = jest.fn().mockReturnValue(Promise.resolve()) - const pinRmMock = jest.fn().mockReturnValue(Promise.resolve()) api.setClientInstance({ util: { @@ -333,21 +327,18 @@ describe('api.js', () => { }, object: { put: objectPutMock - }, - pin: { - add: pinAddMock, - rm: pinRmMock } }) const queryGatewaysMock = jest.fn() // act - return api.addFilesFromFSPath(['./textfiles'], queryGatewaysMock) + return api.addFilesFromFSPath(['./textfiles'], false, queryGatewaysMock) .then(result => { // assert + expect(result.hash).toEqual('QmRgutAxd8t7oGkSm4wmeuByG6M51wcTso6cubDdQtu002') + expect(objectPutMock).not.toHaveBeenCalled() expect(queryGatewaysMock).toHaveBeenCalledWith('QmRgutAxd8t7oGkSm4wmeuByG6M51wcTso6cubDdQtu001') expect(queryGatewaysMock).toHaveBeenCalledWith('QmRgutAxd8t7oGkSm4wmeuByG6M51wcTso6cubDdQtu002') - expect(queryGatewaysMock).toHaveBeenCalledWith('QmRgutAxd8t7oGkSm4wmeuByG6M51wcTso6cubDdQtu003') - expect(queryGatewaysMock).toHaveBeenCalledTimes(3) + expect(queryGatewaysMock).toHaveBeenCalledTimes(2) }) }) }) diff --git a/app/windows/Settings/Components/IntegrationsPanel.jsx b/app/windows/Settings/Components/IntegrationsPanel.jsx index 385ece4..db01988 100644 --- a/app/windows/Settings/Components/IntegrationsPanel.jsx +++ b/app/windows/Settings/Components/IntegrationsPanel.jsx @@ -3,19 +3,36 @@ import { observer } from 'mobx-react' import { Pane, CheckBox } from 'react-photonkit' import * as ContextMenu from '../../../lib/os-context-menu/index' import Settings from 'electron-settings' +import styled from 'styled-components' const isWindows = process.platform === 'win32' +const WrappingSetting = styled.div` + .checkbox { + margin-bottom: 0px; + } + + p { + margin-top: 0px; + color: rgb(127,127,127); + font-style: italic; + } +` + @observer class IntegrationsPanel extends React.Component { state = { - contextMenu: false + contextMenu: false, + disableWrapping: false } componentDidMount () { ContextMenu.isRegistered().then(status => { this.setState({ contextMenu: status }) }) + + const disableWrapping = Settings.get('disableWrapping') + this.setState({ disableWrapping }) } _handleContextMenuChange = () => { @@ -32,19 +49,35 @@ class IntegrationsPanel extends React.Component { } } + _handleWrappingChange = () => { + const nextValue = !this.state.disableWrapping + Settings.set('disableWrapping', nextValue) + this.setState({ disableWrapping: nextValue }) + } + render () { if (this.props.navigationStore.selected !== 3) return null if (!this.props.informationStore) return null if (!this.props.informationStore.loaded) return null - if (!isWindows) return null return ( - + { + isWindows && + + } + + +

Wrapping a file will help preserving file names and extensions

+
) } diff --git a/app/windows/Settings/Components/Sidebar.jsx b/app/windows/Settings/Components/Sidebar.jsx index 2538ebd..a38cc82 100644 --- a/app/windows/Settings/Components/Sidebar.jsx +++ b/app/windows/Settings/Components/Sidebar.jsx @@ -3,8 +3,6 @@ import { observer } from 'mobx-react' import { Pane, NavGroup, NavTitle, NavGroupItem } from 'react-photonkit' -const isWindows = process.platform === 'win32' - /** * Render the Sidebar, uses NavigatorStore */ @@ -19,15 +17,10 @@ class Sidebar extends React.Component { Settings and Info, , , - + , + ] - if (isWindows) { - menus.push( - - ) - } - return ( diff --git a/app/windows/Storage/fileIntegration.js b/app/windows/Storage/fileIntegration.js index 08468bf..b23d171 100644 --- a/app/windows/Storage/fileIntegration.js +++ b/app/windows/Storage/fileIntegration.js @@ -41,7 +41,7 @@ export function addFilesPaths (paths) { let promises if (paths.length > 1 && askWhetherToWrapAllFiles()) { // If the user says yes to wrapping all files, simply pass the paths array - promises = [addFilesFromFSPath(paths)] + promises = [addFilesFromFSPath(paths, true)] } else { // User wants to wrap each file (this method expects an array) promises = paths.map(path => addFilesFromFSPath([path]))