feat(archive): ability to archive / extract files in zip format
fix(selectview): clear active files when user taps on select-view button in toolbar resolve #10 resolve #12
This commit is contained in:
parent
44340abb61
commit
dfb7d8aa72
11677
build/main.js
11677
build/main.js
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,9 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/mdibaiee/",
|
"homepage": "https://github.com/mdibaiee/",
|
||||||
"dependencies": {},
|
"dependencies": {
|
||||||
|
"jszip": "2.5.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel": "^5.8.23",
|
"babel": "^5.8.23",
|
||||||
"babelify": "^6.2.0",
|
"babelify": "^6.2.0",
|
||||||
|
15
src/js/actions/compress.js
Normal file
15
src/js/actions/compress.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { COMPRESS, DECOMPRESS } from './types';
|
||||||
|
|
||||||
|
export function compress(file) {
|
||||||
|
return {
|
||||||
|
type: COMPRESS,
|
||||||
|
file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decompress(file) {
|
||||||
|
return {
|
||||||
|
type: DECOMPRESS,
|
||||||
|
file
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,9 @@ const TYPES = {
|
|||||||
REFRESH: Symbol('REFRESH'),
|
REFRESH: Symbol('REFRESH'),
|
||||||
SORT: Symbol('SORT'),
|
SORT: Symbol('SORT'),
|
||||||
|
|
||||||
|
COMPRESS: Symbol('COMPRESS'),
|
||||||
|
DECOMPRESS: Symbol('DECOMPRESS'),
|
||||||
|
|
||||||
NEW_FILE: Symbol('NEW_FILE'),
|
NEW_FILE: Symbol('NEW_FILE'),
|
||||||
CREATE_FILE: Symbol('CREATE_FILE'),
|
CREATE_FILE: Symbol('CREATE_FILE'),
|
||||||
SHARE_FILE: Symbol('SHARE_FILE'),
|
SHARE_FILE: Symbol('SHARE_FILE'),
|
||||||
|
@ -70,6 +70,12 @@ export async function children(dir, gatherInfo) {
|
|||||||
return childs;
|
return childs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function isDirectory(path) {
|
||||||
|
let file = await getFile(path);
|
||||||
|
|
||||||
|
return !(file instanceof Blob);
|
||||||
|
}
|
||||||
|
|
||||||
export async function readFile(path) {
|
export async function readFile(path) {
|
||||||
let file = await getFile(path);
|
let file = await getFile(path);
|
||||||
|
|
||||||
@ -85,6 +91,16 @@ export async function readFile(path) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function writeFile(path, content) {
|
||||||
|
let request = sdcard().addNamed(content, path);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request.onsuccess = resolve;
|
||||||
|
request.onerror = reject;
|
||||||
|
request.onabort = reject;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function createFile(...args) {
|
export async function createFile(...args) {
|
||||||
let parent = await root();
|
let parent = await root();
|
||||||
|
|
||||||
@ -147,11 +163,6 @@ export async function copy(file, newPath) {
|
|||||||
|
|
||||||
let blob = new Blob([content], {type: target.type});
|
let blob = new Blob([content], {type: target.type});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return writeFile(newPath, blob);
|
||||||
let request = sdcard().addNamed(blob, newPath);
|
|
||||||
request.onsuccess = resolve;
|
|
||||||
request.onerror = reject;
|
|
||||||
request.onabort = reject;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@ export default class Breadcrumb extends Component {
|
|||||||
let path = current.join('/').replace(/^\//, ''); // remove starting slash
|
let path = current.join('/').replace(/^\//, ''); // remove starting slash
|
||||||
let key = directories.length + index;
|
let key = directories.length + index;
|
||||||
let style = { zIndex: arr.length - index};
|
let style = { zIndex: arr.length - index};
|
||||||
console.log('history', dir)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span key={key} className='history' onClick={bind(changedir(path))} style={style}>{dir}</span>
|
<span key={key} className='history' onClick={bind(changedir(path))} style={style}>{dir}</span>
|
||||||
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
|||||||
import { refresh, selectView } from 'actions/files-view';
|
import { refresh, selectView } from 'actions/files-view';
|
||||||
import { show as showDialog } from 'actions/dialog';
|
import { show as showDialog } from 'actions/dialog';
|
||||||
import { show as showMenu } from 'actions/menu';
|
import { show as showMenu } from 'actions/menu';
|
||||||
|
import active from 'actions/active-file';
|
||||||
import settings from 'actions/settings';
|
import settings from 'actions/settings';
|
||||||
import store, { bind } from 'store';
|
import store, { bind } from 'store';
|
||||||
import { MENU_WIDTH } from './menu';
|
import { MENU_WIDTH } from './menu';
|
||||||
@ -13,7 +14,7 @@ export default class Toolbar extends Component {
|
|||||||
<button className='icon-back tour-item' onClick={this.goUp} />
|
<button className='icon-back tour-item' onClick={this.goUp} />
|
||||||
<button className='icon-plus tour-item' onClick={this.newFile} />
|
<button className='icon-plus tour-item' onClick={this.newFile} />
|
||||||
<button className='icon-refresh tour-item' onClick={bind(refresh())} />
|
<button className='icon-refresh tour-item' onClick={bind(refresh())} />
|
||||||
<button className='icon-select tour-item' onClick={bind(selectView('toggle'))} />
|
<button className='icon-select tour-item' onClick={this.selectView} />
|
||||||
<button className='icon-more tour-item' onClick={this.showMore.bind(this)} ref='more' />
|
<button className='icon-more tour-item' onClick={this.showMore.bind(this)} ref='more' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -39,6 +40,11 @@ export default class Toolbar extends Component {
|
|||||||
store.dispatch(changedir(up));
|
store.dispatch(changedir(up));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectView() {
|
||||||
|
store.dispatch(selectView('toggle'));
|
||||||
|
store.dispatch(active());
|
||||||
|
}
|
||||||
|
|
||||||
newFile() {
|
newFile() {
|
||||||
let cwd = store.getState().get('cwd');
|
let cwd = store.getState().get('cwd');
|
||||||
let action = showDialog('createDialog', {
|
let action = showDialog('createDialog', {
|
||||||
|
@ -2,6 +2,7 @@ import { hideAll } from 'actions/menu';
|
|||||||
import { show } from 'actions/dialog';
|
import { show } from 'actions/dialog';
|
||||||
import { selectView } from 'actions/files-view';
|
import { selectView } from 'actions/files-view';
|
||||||
import { copy, move } from 'actions/file';
|
import { copy, move } from 'actions/file';
|
||||||
|
import { compress, decompress } from 'actions/compress';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
const entryMenu = {
|
const entryMenu = {
|
||||||
@ -64,6 +65,28 @@ const entryMenu = {
|
|||||||
blob
|
blob
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Extract',
|
||||||
|
enabled() {
|
||||||
|
let active = store.getState().get('activeFile');
|
||||||
|
|
||||||
|
if (active) console.log(active[0].name);
|
||||||
|
return active && active[0].name.indexOf('.zip') > -1;
|
||||||
|
},
|
||||||
|
action() {
|
||||||
|
let active = store.getState().get('activeFile');
|
||||||
|
|
||||||
|
store.dispatch(decompress(active));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Archive',
|
||||||
|
action() {
|
||||||
|
let active = store.getState().get('activeFile');
|
||||||
|
|
||||||
|
store.dispatch(compress(active));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -142,6 +165,14 @@ const moreMenu = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Archive',
|
||||||
|
action() {
|
||||||
|
let active = store.getState().get('activeFile');
|
||||||
|
|
||||||
|
store.dispatch(compress(active));
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
import { ACTIVE_FILE, CHANGE_DIRECTORY } from 'actions/types';
|
import { ACTIVE_FILE, SELECT_VIEW } from 'actions/types';
|
||||||
|
|
||||||
export default function(state = null, action) {
|
export default function(state = null, action) {
|
||||||
if (action.type === ACTIVE_FILE) {
|
if (action.type === ACTIVE_FILE) {
|
||||||
return action.file;
|
return action.file;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === CHANGE_DIRECTORY) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { LIST_FILES, RENAME_FILE, DELETE_FILE, CREATE_FILE, MOVE_FILE, COPY_FILE, SEARCH } from 'actions/types';
|
import { LIST_FILES, RENAME_FILE, DELETE_FILE, CREATE_FILE, MOVE_FILE, COPY_FILE, SEARCH, COMPRESS, DECOMPRESS } from 'actions/types';
|
||||||
|
import zip from 'jszip';
|
||||||
import { refresh } from 'actions/files-view';
|
import { refresh } from 'actions/files-view';
|
||||||
import { move, remove, sdcard, createFile, createDirectory, copy } from 'api/files';
|
import { move, remove, sdcard, createFile, readFile, writeFile, createDirectory, getFile, copy, children } from 'api/files';
|
||||||
import { show } from 'actions/dialog';
|
import { show } from 'actions/dialog';
|
||||||
import store, { bind } from 'store';
|
import store, { bind } from 'store';
|
||||||
import { reportError, type } from 'utils';
|
import { reportError, type, normalize } from 'utils';
|
||||||
|
|
||||||
let boundRefresh = bind(refresh());
|
let boundRefresh = bind(refresh());
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ export default function(state = [], action) {
|
|||||||
|
|
||||||
if (action.type === DELETE_FILE) {
|
if (action.type === DELETE_FILE) {
|
||||||
let all = Promise.all(action.file.map(file => {
|
let all = Promise.all(action.file.map(file => {
|
||||||
let path = ((file.path || '') + file.name).replace(/^\//, '');
|
let path = normalize((file.path || '') + file.name);
|
||||||
return remove(path, true);
|
return remove(path, true);
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -82,6 +83,69 @@ export default function(state = [], action) {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type === COMPRESS) {
|
||||||
|
let archive = new zip();
|
||||||
|
let cwd = store.getState().get('cwd');
|
||||||
|
|
||||||
|
let all = Promise.all(action.file.map(function addFile(file) {
|
||||||
|
console.log('addFile', file);
|
||||||
|
let path = normalize((file.path || '') + file.name);
|
||||||
|
let archivePath = path.slice(cwd.length);
|
||||||
|
// directory
|
||||||
|
if (!(file instanceof Blob)) {
|
||||||
|
let folder = archive.folder(file.name);
|
||||||
|
|
||||||
|
return children(path).then(files => {
|
||||||
|
return Promise.all(files.map(child => {
|
||||||
|
return addFile(child);
|
||||||
|
|
||||||
|
// return readFile(childPath).then(content => {
|
||||||
|
// let blob = new Blob([content]);
|
||||||
|
// folder.file(child.name, blob);
|
||||||
|
// });
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return readFile(path).then(content => {
|
||||||
|
archive.file(archivePath + '/' + file.name, content);
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
|
||||||
|
all.then(() => {
|
||||||
|
let buffer = archive.generate({ type: 'nodebuffer' });
|
||||||
|
console.log(buffer);
|
||||||
|
let blob = new Blob([buffer], { type: 'application/zip' });
|
||||||
|
|
||||||
|
let cwd = store.getState().get('cwd');
|
||||||
|
let path = normalize(cwd + '/archive.zip');
|
||||||
|
return writeFile(path, blob);
|
||||||
|
}).then(boundRefresh).catch(reportError);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === DECOMPRESS) {
|
||||||
|
let file = action.file[0];
|
||||||
|
let path = normalize((file.path || '') + file.name);
|
||||||
|
readFile(path).then(content => {
|
||||||
|
let archive = new zip(content);
|
||||||
|
let files = Object.keys(archive.files);
|
||||||
|
|
||||||
|
let all = Promise.all(files.map(name => {
|
||||||
|
let buffer = archive.files[name].asArrayBuffer();
|
||||||
|
let blob = new Blob([buffer]);
|
||||||
|
|
||||||
|
let cwd = store.getState().get('cwd');
|
||||||
|
let filePath = normalize(cwd + '/' + name);
|
||||||
|
|
||||||
|
return writeFile(filePath, blob);
|
||||||
|
}));
|
||||||
|
|
||||||
|
all.then(boundRefresh, reportError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user