fix hawk: a lot of bugfixes here and there
This commit is contained in:
@ -32,17 +32,19 @@ export async function children(dir, gatherInfo) {
|
||||
|
||||
if (gatherInfo) {
|
||||
for (let child of childs) {
|
||||
if (type(child) !== 'Directory') continue;
|
||||
if (type(child) === 'Directory') {
|
||||
let subchildren;
|
||||
try {
|
||||
subchildren = await child.getFilesAndDirectories();
|
||||
} catch(e) {
|
||||
subchildren = [];
|
||||
}
|
||||
|
||||
let subchildren;
|
||||
try {
|
||||
subchildren = await child.getFilesAndDirectories();
|
||||
} catch(e) {
|
||||
subchildren = [];
|
||||
child.children = subchildren.length;
|
||||
} else {
|
||||
child.path = dir + '/';
|
||||
}
|
||||
|
||||
child.children = subchildren.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return childs;
|
||||
@ -108,7 +110,7 @@ export async function copy(file, newPath) {
|
||||
child.path = oldPath + '/';
|
||||
}
|
||||
|
||||
await move(child, newPath + '/' + child.name);
|
||||
await copy(child, newPath + '/' + child.name);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1,13 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import changedir from 'actions/changedir';
|
||||
import { show } from 'actions/menu';
|
||||
import { active } from 'actions/file';
|
||||
import { MENU_WIDTH } from './menu';
|
||||
import store from 'store';
|
||||
import entry from './mixins/entry';
|
||||
|
||||
const MENU_TOP_SPACE = 20;
|
||||
|
||||
export default class Directory extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -19,7 +19,7 @@ export default class FileList extends Component {
|
||||
let settings = store.getState().get('settings');
|
||||
|
||||
let els = files.map((file, index) => {
|
||||
let selected = activeFile.length && activeFile.indexOf(file) > -1;
|
||||
let selected = activeFile.indexOf(file) > -1;
|
||||
if (type(file) === 'File') {
|
||||
return <File selectView={selectView} selected={selected} key={index} index={index} name={file.name} size={file.size} />;
|
||||
} else {
|
||||
|
@ -1,13 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import { show } from 'actions/menu';
|
||||
import { active } from 'actions/file';
|
||||
import { MENU_WIDTH } from './menu';
|
||||
import store from 'store';
|
||||
import { humanSize } from 'utils';
|
||||
import entry from './mixins/entry';
|
||||
|
||||
const MENU_TOP_SPACE = 20;
|
||||
|
||||
export default class File extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
@ -19,7 +14,7 @@ export default class File extends Component {
|
||||
|
||||
let input, label;
|
||||
if (this.props.selectView) {
|
||||
input = <input type='checkbox' id={checkId} defaultChecked={this.props.selected} readOnly />;
|
||||
input = <input type='checkbox' id={checkId} checked={this.props.selected} readOnly />;
|
||||
label = <label htmlFor={checkId}></label>;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ export const MENU_WIDTH = 245;
|
||||
|
||||
export default class Menu extends Component {
|
||||
render() {
|
||||
let { items, active, style } = this.props;
|
||||
let { items, active, style, id } = this.props;
|
||||
items = items || [];
|
||||
|
||||
let els = items.map((item, index) => {
|
||||
@ -16,7 +16,7 @@ export default class Menu extends Component {
|
||||
let className = 'menu ' + (active ? 'active' : '');
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<div className={className} style={style} id={id}>
|
||||
<ul>{els}</ul>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,3 +1,10 @@
|
||||
import React from 'react';
|
||||
import { active } from 'actions/file';
|
||||
import { show } from 'actions/menu';
|
||||
import { MENU_WIDTH } from 'components/menu';
|
||||
|
||||
const MENU_TOP_SPACE = 20;
|
||||
|
||||
export default {
|
||||
contextMenu(e) {
|
||||
e.preventDefault();
|
||||
@ -8,12 +15,20 @@ export default {
|
||||
|
||||
let left = x + width / 2 - MENU_WIDTH / 2,
|
||||
top = y + height / 2 + MENU_TOP_SPACE;
|
||||
|
||||
let dialogHeight = document.getElementById('fileMenu').offsetHeight;
|
||||
|
||||
let diff = window.innerHeight - (dialogHeight + top);
|
||||
if (diff <= 0) {
|
||||
top -= Math.abs(diff);
|
||||
}
|
||||
|
||||
store.dispatch(show('fileMenu', {style: {left, top}}));
|
||||
store.dispatch(active([file]));
|
||||
},
|
||||
|
||||
select() {
|
||||
let current = store.getState().get('activeFile').slice(0);
|
||||
let current = (store.getState().get('activeFile') || []).slice(0);
|
||||
let file = store.getState().get('files')[this.props.index];
|
||||
|
||||
if (current.indexOf(file) > -1) {
|
||||
|
@ -17,19 +17,19 @@ export default class Navigation extends Component {
|
||||
<p>Filter</p>
|
||||
<ul>
|
||||
<li>
|
||||
<input id='filter-all' name='filter' value='' type='radio' defaultChecked={!settings.filter} />
|
||||
<input id='filter-all' name='filter' data-value='' type='radio' defaultChecked={!settings.filter} />
|
||||
<label htmlFor='filter-all'>All</label>
|
||||
</li>
|
||||
<li>
|
||||
<input id='filter-image' name='filter' value='image' type='radio' defaultChecked={settings.filter === 'image'} />
|
||||
<input id='filter-image' name='filter' data-value='image' type='radio' defaultChecked={settings.filter === 'image'} />
|
||||
<label htmlFor='filter-image'>Image</label>
|
||||
</li>
|
||||
<li>
|
||||
<input id='filter-video' name='filter' value='video' type='radio' defaultChecked={settings.filter === 'video'} />
|
||||
<input id='filter-video' name='filter' data-value='video' type='radio' defaultChecked={settings.filter === 'video'} />
|
||||
<label htmlFor='filter-video'>Video</label>
|
||||
</li>
|
||||
<li>
|
||||
<input id='filter-audio' name='filter' value='audio' type='radio' defaultChecked={settings.filter === 'audio'} />
|
||||
<input id='filter-audio' name='filter' data-value='audio' type='radio' defaultChecked={settings.filter === 'audio'} />
|
||||
<label htmlFor='filter-audio'>Audio</label>
|
||||
</li>
|
||||
</ul>
|
||||
@ -77,7 +77,7 @@ export default class Navigation extends Component {
|
||||
|
||||
onChange(e) {
|
||||
let key = e.target.name || e.target.id;
|
||||
let value = e.target.value === undefined ? e.target.checked : e.target.value;
|
||||
let value = typeof e.target.dataset.value !== 'undefined' ? e.target.dataset.value : e.target.checked;
|
||||
|
||||
let action = updateSettings({
|
||||
[key]: value
|
||||
|
@ -19,7 +19,7 @@ window.store = store;
|
||||
window.changedir = changedir;
|
||||
|
||||
let FileMenu = connect(state => state.get('fileMenu'))(Menu);
|
||||
let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
|
||||
// let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
|
||||
let MoreMenu = connect(state => state.get('moreMenu'))(Menu);
|
||||
|
||||
let RenameDialog = connect(state => state.get('renameDialog'))(Dialog);
|
||||
@ -31,16 +31,16 @@ let SearchDialog = connect(state => state.get('searchDialog'))(Dialog);
|
||||
export default class Root extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div onTouchStart={this.touchStart.bind(this)} onClick={this.onClick.bind(this)}>
|
||||
<div onTouchStart={this.touchStart.bind(this)}
|
||||
onClick={this.onClick.bind(this)}>
|
||||
<Header />
|
||||
<Breadcrumb />
|
||||
<Navigation />
|
||||
<FileList />
|
||||
<Toolbar />
|
||||
|
||||
<FileMenu />
|
||||
<DirectoryMenu />
|
||||
<MoreMenu />
|
||||
<FileMenu id='fileMenu' />
|
||||
<MoreMenu id='moreMenu' />
|
||||
|
||||
<RenameDialog />
|
||||
<DeleteDialog />
|
||||
@ -50,6 +50,8 @@ export default class Root extends Component {
|
||||
|
||||
<Spinner />
|
||||
|
||||
<div className='swipe-instruction tour-item'></div>
|
||||
|
||||
<div className='tour-dialog'>
|
||||
Hello! Tap each highlighted button to get an understanding of how they work.
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@ const entryMenu = {
|
||||
action() {
|
||||
let files = store.getState().get('files');
|
||||
let active = store.getState().get('activeFile');
|
||||
const description = `Enter the new name for ${active[0].name}?`;
|
||||
const description = `Enter the new name for ${active[0].name}`;
|
||||
|
||||
store.dispatch(hideAll());
|
||||
store.dispatch(show('renameDialog', {description}));
|
||||
|
@ -46,7 +46,8 @@ export default function(state = [], action) {
|
||||
|
||||
if (action.type === RENAME_FILE) {
|
||||
let all = Promise.all(action.file.map(file => {
|
||||
return move(file, (file.path || '') + action.name);
|
||||
let cwd = store.getState().get('cwd');
|
||||
return move(file, cwd + '/' + action.name);
|
||||
}));
|
||||
|
||||
all.then(boundRefresh, reportError);
|
||||
|
@ -8,7 +8,11 @@ const DEFAULT = {
|
||||
|
||||
export default function(state = DEFAULT, action) {
|
||||
if (action.type === SETTINGS) {
|
||||
return Object.assign({}, state, omit(action, 'type'));
|
||||
let newSettings = Object.assign({}, state, omit(action, 'type'));
|
||||
|
||||
localStorage.setItem('settings', JSON.stringify(newSettings));
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
|
||||
return state;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { SPINNER, CHANGE_DIRECTORY, LIST_FILES, REFRESH, DIALOG, CREATE_FILE, DELETE_FILE } from 'actions/types';
|
||||
import { SPINNER, CHANGE_DIRECTORY, LIST_FILES, REFRESH, DIALOG, SETTINGS,
|
||||
CREATE_FILE, DELETE_FILE, RENAME_FILE, MOVE_FILE, COPY_FILE, SEARCH} from 'actions/types';
|
||||
|
||||
export default function(state = false, action) {
|
||||
if (action.type === SPINNER) {
|
||||
@ -12,6 +13,13 @@ export default function(state = false, action) {
|
||||
switch (action.type) {
|
||||
case CHANGE_DIRECTORY:
|
||||
case REFRESH:
|
||||
case SETTINGS:
|
||||
case CREATE_FILE:
|
||||
case MOVE_FILE:
|
||||
case DELETE_FILE:
|
||||
case RENAME_FILE:
|
||||
case COPY_FILE:
|
||||
case SEARCH:
|
||||
return true;
|
||||
case LIST_FILES:
|
||||
return false;
|
||||
|
@ -7,6 +7,7 @@ import dialogs from './dialogs';
|
||||
|
||||
const DEFAULT = new Immutable.Map(Object.assign({
|
||||
dir: '',
|
||||
settings: JSON.parse(localStorage.getItem('settings') || '{}')
|
||||
}, dialogs, menus));
|
||||
|
||||
let store = createStore(reducers, DEFAULT);
|
||||
|
@ -4,7 +4,8 @@ const MESSAGES = {
|
||||
'icon-select': 'Select files for batch actions',
|
||||
'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
|
||||
'drawer': 'Extra options, tools and links are here',
|
||||
'icon-search': 'Search your storage for a certain file'
|
||||
'icon-search': 'Search your storage for a certain file',
|
||||
'swipe-instruction': 'Swipe from left to right to go to parent folder'
|
||||
}
|
||||
|
||||
const DIALOG_HIDE_DELAY = 2000;
|
||||
|
@ -49,6 +49,40 @@
|
||||
animation: pulse 2s ease-out infinite;
|
||||
}
|
||||
|
||||
.swipe-instruction {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 20%;
|
||||
|
||||
width: 70vw;
|
||||
height: 5rem;
|
||||
|
||||
margin-left: -35vw;
|
||||
|
||||
z-index: 1;
|
||||
|
||||
background: white;
|
||||
|
||||
border-radius: 3rem;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
|
||||
background: darken(white, 15);
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
animation: swipe 3s ease infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.tour-dialog {
|
||||
display: block;
|
||||
|
||||
@ -86,3 +120,22 @@
|
||||
transform: scale(5);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes swipe {
|
||||
80% {
|
||||
left: ~'calc(100% - 5rem)';
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 0;
|
||||
left: ~'calc(100% - 5rem)';
|
||||
}
|
||||
91% {
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
left: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"name": "Hawk",
|
||||
"description": "Keep an eye on your files with a full-featured file-manager",
|
||||
"description": "Keep an eye on your files with a full-featured file manager",
|
||||
"launch_path": "/index.html",
|
||||
"icons": {
|
||||
"16": "/icon/Icon-16.png",
|
||||
@ -15,13 +15,26 @@
|
||||
},
|
||||
"type": "privileged",
|
||||
"permissions": {
|
||||
"device-storage:sdcard": {"access": "readwrite"},
|
||||
"device-storage:videos": {"access": "readwrite"},
|
||||
"device-storage:pictures": {"access": "readwrite"},
|
||||
"device-storage:music": {"access": "readwrite"}
|
||||
"device-storage:sdcard": {
|
||||
"access": "readwrite",
|
||||
"description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
|
||||
},
|
||||
"device-storage:videos": {
|
||||
"access": "readwrite",
|
||||
"description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
|
||||
},
|
||||
"device-storage:pictures": {
|
||||
"access": "readwrite",
|
||||
"description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
|
||||
},
|
||||
"device-storage:music": {
|
||||
"access": "readwrite",
|
||||
"description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
|
||||
}
|
||||
},
|
||||
"installs_allowed_from": [
|
||||
"marketplace.firefox.com"
|
||||
"https://marketplace.firefox.com",
|
||||
"https://marketplace-dev.allizom.org"
|
||||
],
|
||||
"default_locale": "en"
|
||||
}
|
||||
|
Reference in New Issue
Block a user