feat ContextMenu: Rename and Delete
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import { CHANGE_DIRECTORY } from 'actions/types';
|
||||
|
||||
export default function changedir(dir) {
|
||||
if (dir === 'sdcard') dir = '';
|
||||
return {
|
||||
type: CHANGE_DIRECTORY,
|
||||
dir
|
||||
|
32
src/js/actions/dialog.js
Normal file
32
src/js/actions/dialog.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { DIALOG } from 'actions/types';
|
||||
|
||||
export function show(id) {
|
||||
return {
|
||||
type: DIALOG,
|
||||
active: true,
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
export function hide(id) {
|
||||
return {
|
||||
type: DIALOG,
|
||||
active: false,
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
export function toggle(id) {
|
||||
return {
|
||||
type: DIALOG,
|
||||
active: 'toggle',
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
export function hideAll() {
|
||||
return {
|
||||
type: DIALOG,
|
||||
active: false
|
||||
}
|
||||
}
|
35
src/js/actions/file.js
Normal file
35
src/js/actions/file.js
Normal file
@ -0,0 +1,35 @@
|
||||
import { CREATE_FILE, SHARE_FILE, RENAME_FILE, ACTIVE_FILE, DELETE_FILE } from 'actions/types';
|
||||
|
||||
export function create(path, name) {
|
||||
return {
|
||||
type: CREATE_FILE,
|
||||
path, name
|
||||
}
|
||||
}
|
||||
|
||||
export function share() {
|
||||
return {
|
||||
type: SHARE_FILE
|
||||
}
|
||||
}
|
||||
|
||||
export function rename(file, name) {
|
||||
return {
|
||||
type: RENAME_FILE,
|
||||
file, name
|
||||
}
|
||||
}
|
||||
|
||||
export function active(file) {
|
||||
return {
|
||||
type: ACTIVE_FILE,
|
||||
file
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteFile(file) {
|
||||
return {
|
||||
type: DELETE_FILE,
|
||||
file
|
||||
}
|
||||
}
|
29
src/js/actions/files-view.js
Normal file
29
src/js/actions/files-view.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { LIST_FILES, FILES_VIEW, REFRESH } from 'actions/types';
|
||||
import store from 'store';
|
||||
|
||||
export function refresh() {
|
||||
return {
|
||||
type: REFRESH
|
||||
}
|
||||
}
|
||||
|
||||
export function toggle(state) {
|
||||
return {
|
||||
type: FILES_VIEW,
|
||||
view: 'toggle'
|
||||
}
|
||||
}
|
||||
|
||||
export function details(state) {
|
||||
return {
|
||||
type: FILES_VIEW,
|
||||
view: 'details'
|
||||
}
|
||||
}
|
||||
|
||||
export function list(state) {
|
||||
return {
|
||||
type: FILES_VIEW,
|
||||
view: 'list'
|
||||
}
|
||||
}
|
32
src/js/actions/menu.js
Normal file
32
src/js/actions/menu.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { MENU } from 'actions/types';
|
||||
|
||||
export function show(id, x, y) {
|
||||
return {
|
||||
type: MENU,
|
||||
active: true,
|
||||
id, x, y
|
||||
}
|
||||
}
|
||||
|
||||
export function hide(id) {
|
||||
return {
|
||||
type: MENU,
|
||||
active: false,
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
export function toggle(id, x, y) {
|
||||
return {
|
||||
type: MENU,
|
||||
active: 'toggle',
|
||||
id, x, y
|
||||
}
|
||||
}
|
||||
|
||||
export function hideAll() {
|
||||
return {
|
||||
type: MENU,
|
||||
active: false
|
||||
}
|
||||
}
|
22
src/js/actions/navigation.js
Normal file
22
src/js/actions/navigation.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { NAVIGATION, TOGGLE } from 'actions/types';
|
||||
|
||||
export function show() {
|
||||
return {
|
||||
type: NAVIGATION,
|
||||
active: true
|
||||
}
|
||||
}
|
||||
|
||||
export function hide() {
|
||||
return {
|
||||
type: NAVIGATION,
|
||||
active: false
|
||||
}
|
||||
}
|
||||
|
||||
export function toggle() {
|
||||
return {
|
||||
type: NAVIGATION,
|
||||
active: TOGGLE
|
||||
}
|
||||
}
|
@ -1,9 +1,26 @@
|
||||
const TYPES = {
|
||||
CHANGE_DIRECTORY: Symbol(),
|
||||
LIST_FILES: Symbol(),
|
||||
SORT: Symbol(),
|
||||
SEARCH: Symbol(),
|
||||
REFRESH: Symbol()
|
||||
CHANGE_DIRECTORY: Symbol('CHANGE_DIRECTORY'),
|
||||
|
||||
LIST_FILES: Symbol('LIST_FILES'),
|
||||
FILES_VIEW: Symbol('FILES_VIEW'),
|
||||
|
||||
NAVIGATION: Symbol('NAVIGATION'),
|
||||
TOGGLE: Symbol('TOGGLE'),
|
||||
REFRESH: Symbol('REFRESH'),
|
||||
SORT: Symbol('SORT'),
|
||||
|
||||
NEW_FILE: Symbol('NEW_FILE'),
|
||||
CREATE_FILE: Symbol('CREATE_FILE'),
|
||||
SHARE_FILE: Symbol('SHARE_FILE'),
|
||||
RENAME_FILE: Symbol('RENAME_FILE'),
|
||||
ACTIVE_FILE: Symbol('ACTIVE_FILE'),
|
||||
DELETE_FILE: Symbol('DELETE_FILE'),
|
||||
|
||||
MENU: Symbol('MENU'),
|
||||
|
||||
DIALOG: Symbol('DEBUG'),
|
||||
|
||||
SEARCH: Symbol('SEARCH')
|
||||
};
|
||||
|
||||
export default TYPES;
|
||||
|
@ -1,13 +1,86 @@
|
||||
export async function directory(dir = '/') {
|
||||
let storage = navigator.getDeviceStorage('sdcard');
|
||||
let root = await storage.getRoot();
|
||||
import { type } from 'utils';
|
||||
|
||||
if (dir === '/' || !dir) return root;
|
||||
let SD_CACHE;
|
||||
export function sdcard() {
|
||||
if (SD_CACHE) return SD_CACHE;
|
||||
|
||||
return await root.get(dir);
|
||||
SD_CACHE = navigator.getDeviceStorage('sdcard');
|
||||
return SD_CACHE;
|
||||
}
|
||||
|
||||
let ROOT_CACHE;
|
||||
export async function root() {
|
||||
if (ROOT_CACHE) return ROOT_CACHE;
|
||||
|
||||
ROOT_CACHE = await sdcard().getRoot();
|
||||
return ROOT_CACHE;
|
||||
}
|
||||
|
||||
export async function getFile(dir = '/') {
|
||||
let parent = await root();
|
||||
|
||||
if (dir === '/' || !dir) return root();
|
||||
|
||||
return await parent.get(dir);
|
||||
}
|
||||
|
||||
export async function children(dir) {
|
||||
let parent = await directory(dir);
|
||||
let parent = await getFile(dir);
|
||||
return await parent.getFilesAndDirectories();
|
||||
}
|
||||
|
||||
export async function readFile(path) {
|
||||
let file = await getFile(path);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.onabort = reject;
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
export async function createFile(...args) {
|
||||
let parent = await root();
|
||||
|
||||
return await parent.createFile(...args);
|
||||
}
|
||||
|
||||
export async function createDirectory(...args) {
|
||||
let parent = await root();
|
||||
|
||||
return await parent.createDirectory(...args);
|
||||
}
|
||||
|
||||
export async function rename(file, newName) {
|
||||
console.log(file);
|
||||
let path = (file.path || '').slice(1); // remove starting slash
|
||||
let oldPath = (path + file.name);
|
||||
let newPath = path + newName;
|
||||
|
||||
let target = await getFile(oldPath);
|
||||
|
||||
if (type(target) === 'Directory') {
|
||||
await createDirectory(newPath);
|
||||
let childs = await target.getFilesAndDirectories();
|
||||
|
||||
for (let child of childs) {
|
||||
await rename(child, newPath + '/' + child.name);
|
||||
}
|
||||
|
||||
target.delete();
|
||||
return;
|
||||
} else {
|
||||
let content = await readFile(fullpath);
|
||||
|
||||
let blob = new Blob([content], {type: target.type});
|
||||
|
||||
sdcard().delete(fullpath);
|
||||
|
||||
sdcard().addNamed(blob, path + newName);
|
||||
}
|
||||
}
|
||||
|
53
src/js/components/breadcrumb.js
Normal file
53
src/js/components/breadcrumb.js
Normal file
@ -0,0 +1,53 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import changedir from 'actions/changedir';
|
||||
import { bind } from 'store';
|
||||
|
||||
@connect(props)
|
||||
export default class Breadcrumb extends Component {
|
||||
render() {
|
||||
let directories = this.props.cwd.split('/');
|
||||
directories.unshift('sdcard');
|
||||
|
||||
let els = directories.map((dir, index, arr) => {
|
||||
let path = arr.slice(1, index + 1).join('/');
|
||||
let slash = index > 0 ? '/' : '';
|
||||
|
||||
return (
|
||||
<span key={index} onClick={bind(changedir(path))}>
|
||||
<i>{slash}</i>{dir}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
let lastDirectories = this.props.lwd.split('/');
|
||||
if (lastDirectories.length > directories.length - 1) {
|
||||
lastDirectories.splice(0, directories.length - 1);
|
||||
let history = lastDirectories.map((dir, index, arr) => {
|
||||
let current = directories.slice(1).concat(arr.slice(0, index + 1));
|
||||
let path = current.join('/');
|
||||
|
||||
return (
|
||||
<span key={directories.length + index} className='history' onClick={bind(changedir(path))}>
|
||||
<i>/</i>{dir}
|
||||
</span>
|
||||
)
|
||||
});
|
||||
|
||||
els = els.concat(history);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='breadcrumb'>
|
||||
{els}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function props(state) {
|
||||
return {
|
||||
lwd: state.get('lwd'), // last working directory
|
||||
cwd: state.get('cwd')
|
||||
}
|
||||
}
|
28
src/js/components/dialog.js
Normal file
28
src/js/components/dialog.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
export default class Dialog extends Component {
|
||||
render() {
|
||||
let conditionalInput = this.props.input ? <input ref='input' /> : '';
|
||||
let buttons = this.props.buttons.map((button, i) => {
|
||||
return <button className={button.className + ' btn'} key={i}
|
||||
onClick={button.action.bind(this)}>
|
||||
{button.text}
|
||||
</button>;
|
||||
});
|
||||
|
||||
let className = this.props.active ? 'dialog active' : 'dialog';
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<p className='regular-medium'>{this.props.title}</p>
|
||||
<p className='light-medium'>{this.props.description}</p>
|
||||
|
||||
{conditionalInput}
|
||||
|
||||
<div className='foot'>
|
||||
{buttons}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
39
src/js/components/directory.js
Normal file
39
src/js/components/directory.js
Normal file
@ -0,0 +1,39 @@
|
||||
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';
|
||||
|
||||
const MENU_TOP_SPACE = 20;
|
||||
|
||||
export default class Directory extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className='directory' ref='container'
|
||||
onClick={this.peek.bind(this)}
|
||||
onContextMenu={this.contextMenu.bind(this)}>
|
||||
<i></i>
|
||||
<p>{this.props.name}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
peek() {
|
||||
let file = store.getState().get('files')[this.props.index];
|
||||
|
||||
store.dispatch(changedir(file.path.slice(1) + file.name));
|
||||
}
|
||||
|
||||
contextMenu(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let rect = React.findDOMNode(this.refs.container).getBoundingClientRect();
|
||||
let {x, y, width, height} = rect;
|
||||
|
||||
let left = x + width / 2 - MENU_WIDTH / 2,
|
||||
top = y + height / 2 + MENU_TOP_SPACE;
|
||||
store.dispatch(show('directoryMenu', left, top));
|
||||
store.dispatch(active(this.props.index));
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import File from './file';
|
||||
import Directory from './directory';
|
||||
|
||||
@connect(props)
|
||||
export default class FileList extends Component {
|
||||
@ -9,14 +10,18 @@ export default class FileList extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { cwd, files } = this.props;
|
||||
let { files } = this.props;
|
||||
|
||||
let els = files.map((file, index) => {
|
||||
return <File key={index} index={index} name={file.name} />;
|
||||
if (fileType(file) === 'File') {
|
||||
return <File key={index} index={index} name={file.name} />;
|
||||
} else {
|
||||
return <Directory key={index} index={index} name={file.name} />
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div><strong>cwd: {cwd}</strong>
|
||||
<div className='file-list'>
|
||||
{els}
|
||||
</div>
|
||||
);
|
||||
@ -25,7 +30,6 @@ export default class FileList extends Component {
|
||||
|
||||
function props(state) {
|
||||
return {
|
||||
cwd: state.get('cwd'),
|
||||
files: state.get('files')
|
||||
}
|
||||
}
|
||||
@ -36,3 +40,7 @@ async function getFiles(dir) {
|
||||
|
||||
return await root.getFilesAndDirectories();
|
||||
}
|
||||
|
||||
function fileType(file) {
|
||||
return Object.prototype.toString.call(file).slice(8, -1);
|
||||
}
|
||||
|
@ -1,20 +1,35 @@
|
||||
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 changedir from 'actions/changedir';
|
||||
|
||||
const MENU_TOP_SPACE = 20;
|
||||
|
||||
export default class File extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div onClick={this.peekInside.bind(this)}>
|
||||
<p>{this.props.index}. {this.props.name}</p>
|
||||
<div className='file' ref='container'
|
||||
onContextMenu={this.contextMenu.bind(this)}>
|
||||
<i></i>
|
||||
<p>{this.props.name}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
peekInside() {
|
||||
let file = store.getState().get('files')[this.props.index];
|
||||
contextMenu(e) {
|
||||
e.preventDefault();
|
||||
|
||||
console.log(file);
|
||||
store.dispatch(changedir(file.path.slice(1) + file.name));
|
||||
let rect = React.findDOMNode(this.refs.container).getBoundingClientRect();
|
||||
let {x, y, width, height} = rect;
|
||||
|
||||
let left = x + width / 2 - MENU_WIDTH / 2,
|
||||
top = y + height / 2 + MENU_TOP_SPACE;
|
||||
store.dispatch(show('fileMenu', left, top));
|
||||
store.dispatch(active(this.props.index));
|
||||
}
|
||||
}
|
||||
|
18
src/js/components/header.js
Normal file
18
src/js/components/header.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React, { Component } from 'react';
|
||||
import { toggle } from 'actions/navigation';
|
||||
import store from 'store';
|
||||
|
||||
export default class Header extends Component {
|
||||
render() {
|
||||
return (
|
||||
<header>
|
||||
<button className='drawer' onClick={this.toggleNavigation.bind(this)}></button>
|
||||
<h1 className='regular-medium'>Hawk</h1>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
toggleNavigation() {
|
||||
store.dispatch(toggle());
|
||||
}
|
||||
}
|
21
src/js/components/menu.js
Normal file
21
src/js/components/menu.js
Normal file
@ -0,0 +1,21 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
export const MENU_WIDTH = 245;
|
||||
|
||||
export default class Menu extends Component {
|
||||
render() {
|
||||
let { items, active, style } = this.props;
|
||||
items = items || [];
|
||||
|
||||
let els = items.map((item, index) => {
|
||||
return <li key={index} onClick={item.action.bind(this)}>{item.name}</li>
|
||||
});
|
||||
let className = 'menu ' + (active ? 'active' : '');
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<ul>{els}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
43
src/js/components/navigation.js
Normal file
43
src/js/components/navigation.js
Normal file
@ -0,0 +1,43 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { hide } from 'actions/navigation';
|
||||
|
||||
@connect(props)
|
||||
export default class Navigation extends Component {
|
||||
render() {
|
||||
return (
|
||||
<nav className={this.props.active ? 'active' : ''}>
|
||||
<i onClick={this.hide.bind(this)} />
|
||||
|
||||
<p>Filter</p>
|
||||
<ul>
|
||||
<li>Picture</li>
|
||||
<li>Video</li>
|
||||
<li>Audio</li>
|
||||
</ul>
|
||||
|
||||
<p>Tools</p>
|
||||
<ul>
|
||||
<li>FTP Browser</li>
|
||||
</ul>
|
||||
|
||||
<p>Preferences</p>
|
||||
<ul>
|
||||
<li>Show Hidden Files <input type='checkbox' /></li>
|
||||
<li>Show Directories First <input type='checkbox' /></li>
|
||||
<li>Advanced Preferences</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.props.dispatch(hide());
|
||||
}
|
||||
}
|
||||
|
||||
function props(store) {
|
||||
return {
|
||||
active: store.get('navigation')
|
||||
}
|
||||
}
|
@ -1,18 +1,46 @@
|
||||
import React, { Component } from 'react'
|
||||
import FileList from 'components/file-list';
|
||||
import Navigation from 'components/navigation';
|
||||
import Header from 'components/header';
|
||||
import Breadcrumb from 'components/breadcrumb';
|
||||
import Toolbar from 'components/toolbar';
|
||||
import Menu from 'components/menu';
|
||||
import Dialog from 'components/dialog';
|
||||
import { connect } from 'react-redux';
|
||||
import { hideAll } from 'actions/menu';
|
||||
|
||||
import changedir from 'actions/changedir';
|
||||
import store from 'store';
|
||||
|
||||
window.store = store;
|
||||
window.changedir = changedir;
|
||||
|
||||
let FileMenu = connect(state => state.get('fileMenu'))(Menu);
|
||||
let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
|
||||
|
||||
let RenameDialog = connect(state => state.get('renameDialog'))(Dialog);
|
||||
|
||||
export default class Root extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
Hawk!
|
||||
<div onTouchStart={this.touchStart.bind(this)}>
|
||||
<Header />
|
||||
<Breadcrumb />
|
||||
<Navigation />
|
||||
<FileList />
|
||||
<Toolbar />
|
||||
|
||||
<FileMenu />
|
||||
<DirectoryMenu />
|
||||
|
||||
<RenameDialog />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
touchStart(e) {
|
||||
if (!e.target.closest('.menu')) {
|
||||
store.dispatch(hideAll());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
src/js/components/toolbar.js
Normal file
26
src/js/components/toolbar.js
Normal file
@ -0,0 +1,26 @@
|
||||
import React, { Component } from 'react';
|
||||
import { create, share } from 'actions/file';
|
||||
import { toggle as toggleView, refresh } from 'actions/files-view';
|
||||
import { bind } from 'store';
|
||||
|
||||
export default class Toolbar extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className='toolbar'>
|
||||
<button className='icon-plus' onClick={this.newFile} />
|
||||
<button className='icon-view' onClick={bind(toggleView())} />
|
||||
<button className='icon-refresh' onClick={bind(refresh())} />
|
||||
<button className='icon-share' onClick={bind(share())} />
|
||||
<button className='icon-more' onClick={this.showMore} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
showMore() {
|
||||
|
||||
}
|
||||
|
||||
newFile() {
|
||||
|
||||
}
|
||||
}
|
48
src/js/dialogs.js
Normal file
48
src/js/dialogs.js
Normal file
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { hide, hideAll } from 'actions/dialog';
|
||||
import { rename, deleteFile } from 'actions/file';
|
||||
import store, { bind } from 'store';
|
||||
|
||||
export default {
|
||||
renameDialog: {
|
||||
title: 'Rename',
|
||||
description: 'Enter your desired new name',
|
||||
input: true,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
action: bind(hideAll())
|
||||
},
|
||||
{
|
||||
text: 'Rename',
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
|
||||
let activeFile = store.getState().get('activeFile');
|
||||
this.props.dispatch(rename(activeFile, input.value))
|
||||
this.props.dispatch(hideAll());
|
||||
},
|
||||
className: 'success'
|
||||
}
|
||||
]
|
||||
},
|
||||
deleteDialog: {
|
||||
title: 'Delete',
|
||||
description: 'Are you sure you want to remove @activeFile.name?',
|
||||
buttons: [
|
||||
{
|
||||
text: 'No',
|
||||
action: bind(hideAll())
|
||||
},
|
||||
{
|
||||
text: 'Yes',
|
||||
action() {
|
||||
let activeFile = store.getState().get('activeFile');
|
||||
this.props.dispatch(deleteFile(activeFile));
|
||||
this.props.dispatch(hideAll());
|
||||
},
|
||||
className: 'success'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
27
src/js/menus.js
Normal file
27
src/js/menus.js
Normal file
@ -0,0 +1,27 @@
|
||||
import { hideAll } from 'actions/menu';
|
||||
import { show } from 'actions/dialog';
|
||||
import store from 'store';
|
||||
|
||||
const entryMenu = {
|
||||
items: [
|
||||
{
|
||||
name: 'Rename',
|
||||
action() {
|
||||
store.dispatch(hideAll());
|
||||
store.dispatch(show('renameDialog'));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
action() {
|
||||
store.dispatch(hideAll());
|
||||
store.dispatch(show('deleteDialog'))
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default {
|
||||
fileMenu: Object.assign({}, entryMenu),
|
||||
directoryMenu: Object.assign({}, entryMenu)
|
||||
}
|
9
src/js/reducers/active-file.js
Normal file
9
src/js/reducers/active-file.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { ACTIVE_FILE } from 'actions/types';
|
||||
|
||||
export default function(state = -1, action) {
|
||||
if (action.type === ACTIVE_FILE) {
|
||||
return action.file;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
@ -1,10 +1,23 @@
|
||||
import Immutable from 'immutable';
|
||||
import cwd from './cwd';
|
||||
import lwd from './lwd';
|
||||
import files from './files';
|
||||
import navigation from './navigation';
|
||||
import activeFile from './active-file';
|
||||
import menu from './menu';
|
||||
import dialog from './dialog';
|
||||
|
||||
export default function(state = new Immutable.Map(), action) {
|
||||
console.log('action', action);
|
||||
return new Immutable.Map({
|
||||
lwd: lwd(state, action), // last working directory
|
||||
cwd: cwd(state.get('cwd'), action),
|
||||
files: files(state.get('files'), action)
|
||||
files: files(state.get('files'), action),
|
||||
activeFile: activeFile(state.get('activeFile'), action),
|
||||
navigation: navigation(state.get('navigation'), action),
|
||||
fileMenu: menu(state, action, 'fileMenu'),
|
||||
directoryMenu: menu(state, action, 'directoryMenu'),
|
||||
renameDialog: dialog(state, action, 'renameDialog'),
|
||||
deleteDialog: dialog(state, action, 'deleteDialog')
|
||||
});
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
import { CHANGE_DIRECTORY } from 'actions/types';
|
||||
import { CHANGE_DIRECTORY, REFRESH } from 'actions/types';
|
||||
import listFiles from 'actions/list-files';
|
||||
import { children } from 'api/files';
|
||||
import store from 'store';
|
||||
|
||||
export default function(state = '/', action) {
|
||||
switch (action.type) {
|
||||
case CHANGE_DIRECTORY:
|
||||
children(action.dir).then(files => {
|
||||
store.dispatch(listFiles(files));
|
||||
});
|
||||
return action.dir;
|
||||
default:
|
||||
return state;
|
||||
export default function(state = '', action) {
|
||||
if (action.type === CHANGE_DIRECTORY) {
|
||||
children(action.dir).then(files => {
|
||||
store.dispatch(listFiles(files));
|
||||
});
|
||||
return action.dir;
|
||||
}
|
||||
|
||||
if (action.type === REFRESH) {
|
||||
children(state).then(files => {
|
||||
store.dispatch(listFiles(files));
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
22
src/js/reducers/dialog.js
Normal file
22
src/js/reducers/dialog.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { DIALOG } from 'actions/types';
|
||||
import Immutable from 'immutable';
|
||||
|
||||
export default function(state = new Immutable.Map({}), action, id) {
|
||||
if (action.type === DIALOG) {
|
||||
// action applied to all dialogs
|
||||
if (!action.id) {
|
||||
return Object.assign({}, state.get(id), {active: action.active});
|
||||
}
|
||||
|
||||
if (action.id !== id) return state.get(id);
|
||||
|
||||
let target = state.get(action.id);
|
||||
let active = action.active === 'toggle' ? !target.get('active') : action.active;
|
||||
|
||||
let style = Object.assign({}, state.style, {left: action.x, top: action.y});
|
||||
|
||||
return Object.assign({}, target, { style, active });
|
||||
} else {
|
||||
return state.get(id);
|
||||
}
|
||||
}
|
@ -1,10 +1,29 @@
|
||||
import { LIST_FILES } from 'actions/types';
|
||||
import { LIST_FILES, RENAME_FILE, DELETE_FILE } from 'actions/types';
|
||||
import { refresh } from 'actions/files-view';
|
||||
import { rename, sdcard } from 'api/files';
|
||||
|
||||
export default function(state = [], action) {
|
||||
switch (action.type) {
|
||||
case LIST_FILES:
|
||||
return action.files;
|
||||
default:
|
||||
return state;
|
||||
if (action.type === LIST_FILES) {
|
||||
return action.files;
|
||||
}
|
||||
|
||||
|
||||
if (action.type === RENAME_FILE) {
|
||||
let file = state[action.file];
|
||||
|
||||
rename(file, action.name).then(refresh);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
if (action.type === DELETE_FILE) {
|
||||
let file = state[action.file];
|
||||
|
||||
sdcard().delete((file.path || '') + '/' + file.name);
|
||||
let copy = state.slice(0);
|
||||
copy.splice(action.file, 1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
8
src/js/reducers/lwd.js
Normal file
8
src/js/reducers/lwd.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { CHANGE_DIRECTORY } from 'actions/types';
|
||||
|
||||
export default function(state = '', action) {
|
||||
if (action.type === CHANGE_DIRECTORY) {
|
||||
return state.get('cwd');
|
||||
}
|
||||
return state.get('lwd');
|
||||
}
|
22
src/js/reducers/menu.js
Normal file
22
src/js/reducers/menu.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { MENU } from 'actions/types';
|
||||
import Immutable from 'immutable';
|
||||
|
||||
export default function(state = new Immutable.Map({}), action, id) {
|
||||
if (action.type === MENU) {
|
||||
// action applied to all menus
|
||||
if (!action.id) {
|
||||
return Object.assign({}, state.get(id), {active: action.active});
|
||||
}
|
||||
|
||||
if (action.id !== id) return state.get(id);
|
||||
|
||||
let target = state.get(action.id);
|
||||
let active = action.active === 'toggle' ? !target.get('active') : action.active;
|
||||
|
||||
let style = Object.assign({}, state.style, {left: action.x, top: action.y});
|
||||
|
||||
return Object.assign({}, target, { style, active });
|
||||
} else {
|
||||
return state.get(id);
|
||||
}
|
||||
}
|
9
src/js/reducers/navigation.js
Normal file
9
src/js/reducers/navigation.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { NAVIGATION, TOGGLE } from 'actions/types';
|
||||
|
||||
export default function(state = false, action) {
|
||||
if (action.type === NAVIGATION) {
|
||||
return action.active === TOGGLE ? !state : action.active;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
@ -2,13 +2,19 @@ import { createStore } from 'redux';
|
||||
import reducers from 'reducers/all';
|
||||
import changedir from 'actions/changedir';
|
||||
import Immutable from 'immutable';
|
||||
import menus from './menus';
|
||||
import dialogs from './dialogs';
|
||||
|
||||
const DEFAULT = new Immutable.Map({
|
||||
dir: '/',
|
||||
const DEFAULT = new Immutable.Map(Object.assign({
|
||||
dir: '',
|
||||
files: []
|
||||
});
|
||||
}, dialogs, menus));
|
||||
|
||||
let store = createStore(reducers, DEFAULT);
|
||||
store.dispatch(changedir(DEFAULT.dir));
|
||||
store.dispatch(changedir(DEFAULT.get('dir')));
|
||||
|
||||
export function bind(action) {
|
||||
return () => store.dispatch(action);
|
||||
}
|
||||
|
||||
export default store;
|
||||
|
3
src/js/utils.js
Normal file
3
src/js/utils.js
Normal file
@ -0,0 +1,3 @@
|
||||
export function type(obj) {
|
||||
return Object.prototype.toString.call(obj).slice(8, -1);
|
||||
}
|
Reference in New Issue
Block a user