maintenance (structure): Full revamp of the project code structure

This commit is contained in:
Mickael KERJEAN
2018-03-02 13:53:53 +11:00
parent 25683b3b56
commit f093f00a4b
128 changed files with 1720 additions and 2666 deletions

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

@ -10,7 +10,7 @@ Call it an FTP client, an S3 viewer or a Dropbox like web app, Nuage leverages y
* Demo
[[https://nuage.kerjean.me][Try]] before install
[[https://raw.githubusercontent.com/mickael-kerjean/nuage/master/server/public/img/photo.jpg]]
[[https://raw.githubusercontent.com/mickael-kerjean/nuage/master/.assets/img/photo.jpg]]
* Features
- manage your files directly from your browser
- listen to music

View File

@ -27,17 +27,6 @@ body, html{
}
a{color: inherit; text-decoration: none;}
.scroll-y{
overflow-y: scroll!important;
overflow-x: hidden!important;
-webkit-overflow-scrolling: touch;
}
.scroll-x{
overflow-x: scroll!important;
overflow-y: hidden!important;
-webkit-overflow-scrolling: touch;
}
select{-moz-appearance: none;}
select:-moz-focusring {
color: inherit;

View File

@ -0,0 +1,11 @@
.video-js{outline: none;}
.video-js .vjs-big-play-button:before, .video-js .vjs-control:before, .video-js .vjs-modal-dialog, .vjs-modal-dialog .vjs-modal-dialog-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%; }
.video-js .vjs-big-play-button:before, .video-js .vjs-control:before {
text-align: center; }

View File

Before

Width:  |  Height:  |  Size: 676 B

After

Width:  |  Height:  |  Size: 676 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 747 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 730 B

After

Width:  |  Height:  |  Size: 730 B

View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

View File

Before

Width:  |  Height:  |  Size: 706 B

After

Width:  |  Height:  |  Size: 706 B

View File

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

View File

Before

Width:  |  Height:  |  Size: 932 B

After

Width:  |  Height:  |  Size: 932 B

View File

Before

Width:  |  Height:  |  Size: 904 B

After

Width:  |  Height:  |  Size: 904 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Input, Button, Modal, NgIf } from './';
import './prompt.scss';
export class Alert extends React.Component {
constructor(props){
super(props);
this.state = {
modal_appear: false
};
}
onSubmit(e){
e.preventDefault();
this.props.onConfirm();
this.setState({modal_appear: false});
}
render() {
return (
<Modal isActive={this.state.modal_appear} onQuit={this.onSubmit.bind(this)}>
<div className="component_alert">
<p>
{this.props.message}
</p>
<form id="key_manager" onSubmit={this.onSubmit.bind(this)}>
<div className="buttons">
<Button type="submit" onClick={this.onSubmit.bind(this)}>OK</Button>
</div>
</form>
</div>
</Modal>
);
}
}
Alert.propTypes = {
message: PropTypes.string.isRequired,
onConfirm: PropTypes.func
};

View File

@ -1 +0,0 @@
import React from 'react';

View File

@ -1,9 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom'
import { theme, to_rgba } from '../utilities/theme';
import { NgIf, Icon } from '../utilities/';
import { EventEmitter, EventReceiver } from '../data';
import { NgIf, Icon, EventEmitter, EventReceiver } from './';
export class BreadCrumb extends React.Component {
constructor(props){
@ -63,8 +61,8 @@ BreadCrumb.propTypes = {
const BreadCrumbContainer = (props) => {
let style1 = {background: theme.component.breadcrumb.bg, margin: '0 0 0px 0', padding: '6px 0', boxShadow: '0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2)', zIndex: '1000', position: 'relative'};
let style2 = {margin: '0 auto', width: '95%', maxWidth: '800px', padding: '0', color: theme.component.breadcrumb.color};
let style1 = {background: 'white', margin: '0 0 0px 0', padding: '6px 0', boxShadow: '0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2)', zIndex: '1000', position: 'relative'};
let style2 = {margin: '0 auto', width: '95%', maxWidth: '800px', padding: '0', color: 'rgba(#6f6f6f, 0.8)'};
return (
<div className={props.className} style={style1}>
<ul style={style2}>
@ -144,7 +142,7 @@ export class PathElementWrapper extends React.Component {
render(){
let style = {
cursor: this.props.isLast ? '' : 'pointer',
background: this.state.hover && this.props.isLast !== true? theme.effects.hover : 'inherit',
background: this.state.hover && this.props.isLast !== true? '#f5f5f5' : 'inherit',
borderRadius: '1px',
fontSize: '18px',
display: 'inline-block',
@ -152,8 +150,8 @@ export class PathElementWrapper extends React.Component {
fontWeight: this.props.isLast ? '100': ''
};
if(this.props.highlight === true){
style.background = theme.effects.selected;
style.border = '2px solid '+theme.colors.primary;
style.background = '#c5e2f1';
style.border = '2px solid #9AD1ED';
style.borderRadius = '2px';
style.padding = '2px 20px';
}
@ -175,7 +173,7 @@ export class PathElement extends PathElementWrapper {
render(highlight = false){
return (
<div style={{display: 'inline-block', color: this.props.isLast? theme.component.breadcrumb.last : 'inherit'}}>
<div style={{display: 'inline-block', color: this.props.isLast? '#6f6f6f' : 'inherit'}}>
<PathElementWrapper highlight={highlight} {...this.props} />
</div>
)

View File

View File

@ -1,8 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { theme } from './theme';
import './buttons.scss';
import './button.scss';
export class Button extends React.Component {
constructor(props){
@ -21,4 +20,5 @@ export class Button extends React.Component {
}
Button.propTypes = {
theme: PropTypes.string
};

View File

@ -1,6 +1,4 @@
import React from 'react';
import {theme} from './theme';
import './card.scss';
export class Card extends React.Component {

View File

@ -1,15 +0,0 @@
import React from 'react';
export class Connect extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<div>
CONNECT
</div>
);
}
}

View File

@ -34,7 +34,6 @@ function emit(event, payload){
}
}
export function EventReceiver(WrappedComponent){
let id = Math.random().toString();
@ -53,8 +52,6 @@ export function EventReceiver(WrappedComponent){
}
}
export function EventEmitter(WrappedComponent) {
return class extends React.Component {
emit(){

63
client/components/icon.js Normal file
View File

@ -0,0 +1,63 @@
import React from 'react';
import './icon.scss';
import img_folder from "../assets/img/folder.svg";
import img_file from "../assets/img/file.svg";
import img_loader from "../assets/img/loader.svg";
import img_save from "../assets/img/save.svg";
import img_power from "../assets/img/power.svg";
import img_edit from "../assets/img/edit.svg";
import img_delete from "../assets/img/delete.svg";
import img_bucket from "../assets/img/bucket.svg";
import img_link from "../assets/img/link.svg";
import img_loading from "../assets/img/loader.svg";
import img_download from "../assets/img/download.svg";
import img_play from "../assets/img/play.svg";
import img_pause from "../assets/img/pause.svg";
import img_error from "../assets/img/error.svg";
import img_loading_white from "../assets/img/loader_white.svg";
export const Icon = (props) => {
let img;
if(props.name === 'directory'){
img = img_folder;
}else if(props.name === 'file'){
img = img_file;
}else if(props.name === 'loader'){
img = img_loader;
}else if(props.name === 'save'){
img = img_save;
}else if(props.name === 'power'){
img = img_power;
}else if(props.name === 'edit'){
img = img_edit;
}else if(props.name === 'delete'){
img = img_delete;
}else if(props.name === 'bucket'){
img = img_bucket;
}else if(props.name === 'link'){
img = img_link;
}else if(props.name === 'loading'){
img = img_loader;
}else if(props.name === 'download'){
img = img_download;
}else if(props.name === 'play'){
img = img_play;
}else if(props.name === 'pause'){
img = img_pause;
}else if(props.name === 'error'){
img = img_error;
}else if(props.name === 'loading_white'){
img = img_loading_white;
}else{
throw('unknown icon');
}
return (
<img className="component_icon"
style={props.style}
onClick={props.onClick}
src={img}
alt={props.name}/>
);
}

View File

@ -1,5 +1,21 @@
export { BreadCrumb } from './breadcrumb';
export { FileSystem } from './filesystem';
export { Connect } from './connect';
export { EventEmitter, EventReceiver } from './events';
export { BreadCrumb, PathElement } from './breadcrumb';
export { Input } from './input';
export { Textarea } from './textarea';
export { Button } from './button';
export { Container } from './container';
export { NgIf } from './ngif';
export { Card } from './card';
export { Loader } from './loader';
export { Error } from './error';
export { Fab } from './fab';
export { Icon } from './icon';
export { Notification } from './notification';
export { Uploader } from './uploader';
export { Bundle } from './bundle';
export { Modal } from './modal';
export { Prompt } from './prompt';
export { Alert } from './alert';
//export { Connect } from './connect';
// Those are commented because they will delivered as a separate chunk
// export { Editor } from './editor';

View File

@ -1,11 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { theme } from './theme';
import { Input } from './input';
import { Button } from './button';
import { NgIf } from './ngif';
import './modal.scss';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import PropTypes from 'prop-types';
import { Input, Button, NgIf } from './';
import './modal.scss';
export class Modal extends React.Component {
constructor(props){

View File

@ -19,5 +19,5 @@ export class NgIf extends React.Component {
}
NgIf.propTypes = {
cond: PropTypes.bool
cond: PropTypes.bool.isRequired
};

View File

@ -1,8 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { NgIf } from './';
import { theme } from './theme';
import { NgIf } from './';
import './notification.scss';
export class Notification extends React.Component {

View File

@ -0,0 +1,57 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Input, Button, Modal, NgIf } from './';
import './prompt.scss';
export class Prompt extends React.Component {
constructor(props){
super(props);
this.state = {
modal_appear: false,
error: ''
};
}
componentWillReceiveProps(props){
if(props.error !== this.state.error){
this.setState({error: props.error});
}
}
onCancel(should_clear){
this.setState({modal_appear: false});
}
onSubmit(e){
}
render() {
return (
<Modal isActive={this.state.modal_appear} onQuit={this.onCancel.bind(this)}>
<div className="component_prompt">
<p className="message">
{this.props.message}
</p>
<form onSubmit={this.onSubmit.bind(this)}>
<Input autoFocus={true} value={this.state.key} type={this.props.type || 'text'} onChange={this.onKeyChange.bind(this)} autoComplete="new-password" />
<div className="error">{this.props.error}&nbsp;</div>
<div className="buttons">
<Button type="button" onClick={this.onCancel.bind(this)}>CANCEL</Button>
<Button type="submit" theme="secondary" onClick={this.onSubmit.bind(this)}>OK</Button>
</div>
</form>
</div>
</Modal>
);
}
}
Prompt.propTypes = {
type: PropTypes.string,
message: PropTypes.string.isRequired,
onCancel: PropTypes.func,
onConfirm: PropTypes.func
};

View File

@ -0,0 +1,5 @@
.component_prompt{
.message{
text-align: center;
}
}

View File

@ -1,6 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import { theme } from './theme';
import './textarea.scss';

View File

@ -1,5 +0,0 @@
export { Files } from './api';
export { Session } from './api';
export { invalidate } from './tools';
export { opener } from './mimetype';
export { EventEmitter, EventReceiver } from './events';

View File

@ -25,6 +25,7 @@ export function invalidate(url){
throw 'invalidation error';
}
}
export function http_get(url, cache_expire = 0, type = 'json'){
if(cache_expire > 0 && cache[url] && cache[url].date > new Date().getTime()){
return new Promise((done) => done(cache[url].data));

View File

@ -0,0 +1,46 @@
export function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
export function throttle(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};

8
client/helpers/index.js Normal file
View File

@ -0,0 +1,8 @@
export { URL_HOME, goToHome, URL_FILES, goToFiles, URL_VIEWER, goToViewer, URL_LOGIN, goToLogin, URL_LOGOUT, goToLogout } from './navigate';
export { opener } from './mimetype';
export { debounce, throttle } from './backpressure';
export { encrypt, decrypt } from './crypto'
export { pathBuilder } from './path';
export { memory } from './memory';
export { prepare } from './navigate';
export { invalidate, http_get, http_post, http_delete } from './ajax';

View File

@ -29,8 +29,6 @@ export function opener(file){
}
}
const db = {
'pdf': 'application/pdf',
'csv': 'text/csv',

View File

@ -1,5 +1,3 @@
//import { history } from '../history';
export const URL_HOME = '/';
export function goToHome(history){
history.push(URL_HOME);

View File

@ -30,13 +30,8 @@
<meta name="theme-color" content="#f2f2f2">
<meta name="description" content="browse your files in the cloud">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="main"></div>
<link rel="stylesheet" href="/css/codemirror.css">
<link rel="stylesheet" href="/css/codemirror-foldgutter.css">
<link rel="stylesheet" href="/css/videojs-sublime-skin.css">
<link rel="stylesheet" href="/css/video-js.css">
</body>
</html>

View File

@ -2,13 +2,14 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Router from './router';
import './assets/css/reset.scss';
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/cache.js').then(function(registration) {
}).catch(function(error) {
navigator.serviceWorker.register('/cache.js').catch(function(error) {
console.log('ServiceWorker registration failed:', error);
});
}
window.onload = () => {
ReactDOM.render(<Router/>, document.getElementById('main'));
ReactDOM.render(<Router/>, document.getElementById('main'));
};

View File

@ -1,18 +1,6 @@
import { http_get, http_post, http_delete, invalidate } from './tools';
import { prepare } from '../utilities/navigate';
import { http_get, http_post, invalidate, prepare } from '../helpers/';
import Path from 'path';
function invalidate_ls(path, exact = true){
let url = '/api/files/ls?path='.replace(/([^a-zA-Z0-9])/g, '\\$1');
let reg = new RegExp(url + prepare(Path.dirname(path)+'.*'));
return invalidate(reg);
}
function invalidate_cat(path, exact = true){
let url = '/api/files/cat?path='.replace(/([^a-zA-Z0-9])/g, '\\$1');
let reg = new RegExp(url + prepare(path)+ (exact? '' : '.*'));
return invalidate(reg);
}
class FileSystem{
ls(path, cache = 120){
let url = '/api/files/ls?path='+prepare(path);
@ -73,35 +61,15 @@ class FileSystem{
}
}
class SessionManager{
isLogged(){
let url = '/api/session'
return http_get(url);
}
url(type){
if(type === 'dropbox'){
let url = '/api/session/auth/dropbox';
return http_get(url);
}else if(type === 'gdrive'){
let url = '/api/session/auth/gdrive';
return http_get(url);
}else{
return Promise.error({message: 'not authorization backend for: '+type, code: 'UNKNOWN_PROVIDER'})
}
}
authenticate(params){
let url = '/api/session';
return http_post(url, params);
}
logout(){
let url = '/api/session';
return http_delete(url);
}
function invalidate_ls(path, exact = true){
let url = '/api/files/ls?path='.replace(/([^a-zA-Z0-9])/g, '\\$1');
let reg = new RegExp(url + prepare(Path.dirname(path)+'.*'));
return invalidate(reg);
}
function invalidate_cat(path, exact = true){
let url = '/api/files/cat?path='.replace(/([^a-zA-Z0-9])/g, '\\$1');
let reg = new RegExp(url + prepare(path)+ (exact? '' : '.*'));
return invalidate(reg);
}
export const Files = new FileSystem();
export const Session = new SessionManager();

2
client/model/index.js Normal file
View File

@ -0,0 +1,2 @@
export { Files } from './files';
export { Session } from './session';

32
client/model/session.js Normal file
View File

@ -0,0 +1,32 @@
import { http_get, http_post, http_delete } from '../helpers/';
class SessionManager{
isLogged(){
let url = '/api/session'
return http_get(url);
}
url(type){
if(type === 'dropbox'){
let url = '/api/session/auth/dropbox';
return http_get(url);
}else if(type === 'gdrive'){
let url = '/api/session/auth/gdrive';
return http_get(url);
}else{
return Promise.error({message: 'not authorization backend for: '+type, code: 'UNKNOWN_PROVIDER'})
}
}
authenticate(params){
let url = '/api/session';
return http_post(url, params);
}
logout(){
let url = '/api/session';
return http_delete(url);
}
}
export const Session = new SessionManager();

View File

@ -1,12 +1,12 @@
import React from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import config from '../../config.js';
import { Container, NgIf, Loader, Notification, theme } from '../utilities/';
import { Session, invalidate } from '../data';
import { ForkMe, RememberMe, Credentials, Form } from './connectpage/';
import './connectpage.scss';
import { Session } from '../model/';
import { Container, NgIf, Loader, Notification } from '../components/';
import { ForkMe, RememberMe, Credentials, Form } from './connectpage/';
import { invalidate } from '../helpers/';
import config from '../../config.js';
export class ConnectPage extends React.Component {

View File

@ -2,8 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { Input, Button, Modal, NgIf } from '../../utilities';
import { encrypt, decrypt, memory } from '../../utilities';
import { Input, Button, NgIf, Modal } from '../../components/';
import { encrypt, decrypt, memory } from '../../helpers/';
import './credentials.scss';
export class Credentials extends React.Component {

View File

@ -1,10 +1,11 @@
import React from 'react';
import { Container, Card, NgIf, Input, Button, Textarea, Loader, Notification, encrypt, decrypt, theme, Prompt } from '../../utilities';
import { Session, invalidate, password } from '../../data';
import { Container, Card, NgIf, Input, Button, Textarea, Loader, Notification, Prompt } from '../../components/';
import { invalidate, encrypt, decrypt } from '../../helpers/';
import { Session } from '../../model/';
import './form.scss';
import img_drive from '../../assets/google-drive.png';
import img_dropbox from '../../assets/dropbox.png';
import img_drive from '../../assets/img/google-drive.png';
import img_dropbox from '../../assets/img/dropbox.png';
export class Form extends React.Component {
constructor(props){

View File

@ -62,3 +62,9 @@
transition: all 0.3s ease-out;
transition-delay: 0.5s;
}
.scroll-x{
overflow-x: auto!important;
overflow-y: hidden!important;
-webkit-overflow-scrolling: touch;
}

View File

@ -1,14 +1,14 @@
import React from 'react';
import { FileSystem } from '../components/';
import BreadCrumb from './filespage/breadcrumb';
import { Files, EventReceiver } from '../data/';
import { NgIf, Loader, Error, debounce, goToFiles, goToViewer, Uploader } from '../utilities';
import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import { default as TouchBackend } from 'react-dnd-touch-backend';
import Path from 'path';
import './filespage.scss';
import { Files } from '../model/';
import { NgIf, Loader, Error, Uploader, EventReceiver } from '../components/';
import { debounce, goToFiles, goToViewer } from '../helpers/';
import { BreadCrumb, FileSystem } from './filespage/';
@EventReceiver
@DragDropContext(('ontouchstart' in window)? HTML5Backend : HTML5Backend)
@ -25,7 +25,13 @@ export class FilesPage extends React.Component {
this.resetHeight = debounce(this.resetHeight.bind(this), 100);
this.goToFiles = goToFiles.bind(null, this.props.history);
this.goToViewer = goToViewer.bind(null, this.props.history);
}
componentWillMount(){
this.onPathUpdate(this.state.path, 'directory', true);
}
componentDidMount(){
// subscriptions
this.props.subscribe('file.select', this.onPathUpdate.bind(this));
this.props.subscribe('file.upload', this.onUpload.bind(this));
@ -33,15 +39,9 @@ export class FilesPage extends React.Component {
this.props.subscribe('file.rename', this.onRename.bind(this));
this.props.subscribe('file.delete', this.onDelete.bind(this));
this.props.subscribe('file.refresh', this.onRefresh.bind(this));
}
componentWillMount(){
this.onPathUpdate(this.state.path, 'directory', true)
}
componentDidMount(){
this.resetHeight();
this.setState({error: false});
this.hideError();
window.addEventListener("resize", this.resetHeight);
}
@ -55,28 +55,29 @@ export class FilesPage extends React.Component {
window.removeEventListener("resize", this.resetHeight);
}
hideError(){
this.setState({error: false});
}
onRefresh(path = this.state.path){
this.setState({error: false})
this.setState({error: false});
return Files.ls(path).then((files) => {
this.setState({files: files, loading: false})
this.setState({files: files, loading: false});
}).catch((error) => {
this.setState({error: error});
})
});
}
onPathUpdate(path, type = 'directory', withLoader = true){
window.path = this.props.history;
if(type === 'file'){
this.props.history.push('/view'+path)
this.props.history.push('/view'+path);
}else{
this.setState({path: path, loading: withLoader});
if(path !== this.state.path){
this.props.history.push('/files'+path)
this.props.history.push('/files'+path);
}
return this.onRefresh(path)
return this.onRefresh(path);
}
}
@ -86,7 +87,7 @@ export class FilesPage extends React.Component {
}else if(type === 'directory'){
return Files.mkdir(path);
}else{
return Promise.reject({message: 'internal error: can\'t create a '+type.toString(), code: 'UNKNOWN_TYPE'})
return Promise.reject({message: 'internal error: can\'t create a '+type.toString(), code: 'UNKNOWN_TYPE'});
}
}
onRename(from, to, type){
@ -107,16 +108,16 @@ export class FilesPage extends React.Component {
size: file.size,
icon: 'loading',
virtual: true
}
};
});
const files = JSON.parse(JSON.stringify(this.state.files));
this.setState({files: [].concat(newfiles, files)});
return Promise.resolve(_files);
}
};
const processFile = (file) => {
return this.onCreate(Path.join(path, file.name), 'file', file);
}
};
const updateUI = (filename) => {
const files = JSON.parse(JSON.stringify(this.state.files))
@ -131,10 +132,10 @@ export class FilesPage extends React.Component {
})
.filter((file) => {
return file === null? false : true;
})
});
this.setState({files: files});
return Promise.resolve('ok')
}
return Promise.resolve('ok');
};
const showError = (filename, err) => {
if(err && err.code === 'CANCELLED'){ return }
@ -148,14 +149,14 @@ export class FilesPage extends React.Component {
return file;
});
this.setState({files: files});
return Promise.resolve('ok')
}
return Promise.resolve('ok');
};
function generator(arr){
let store = arr;
return {
next: function(){
return store.pop()
return store.pop();
}
};
}
@ -166,7 +167,7 @@ export class FilesPage extends React.Component {
return processFile(file)
.then((ok) => updateUI(file.name))
.then(() => job(it))
.catch((err) => showError(file.name, err))
.catch((err) => showError(file.name, err));
}else{
return Promise.resolve('ok');
}
@ -185,7 +186,6 @@ export class FilesPage extends React.Component {
.then((res) => Promise.resolve('ok'));
}
resetHeight(){
this.setState({
height: document.body.clientHeight - document.querySelector('.breadcrumb').offsetHeight
@ -195,23 +195,23 @@ export class FilesPage extends React.Component {
render() {
return (
<div>
<BreadCrumb className="breadcrumb" path={this.state.path} />
<div style={{height: this.state.height+'px'}} className="scroll-y">
<NgIf cond={!this.state.loading} style={{padding: '5px 0 20px 0', height: '100%', boxSizing: 'border-box'}}>
<FileSystem path={this.state.path} files={this.state.files} />
<Uploader path={this.state.path} />
<div className="component_page_filespage">
<BreadCrumb className="breadcrumb" path={this.state.path} />
<div style={{height: this.state.height+'px'}} className="scroll-y">
<NgIf className="container" cond={!this.state.loading}>
<FileSystem path={this.state.path} files={this.state.files} />
<Uploader path={this.state.path} />
</NgIf>
<NgIf cond={this.state.loading}>
<NgIf cond={this.state.error === false}>
<Loader/>
</NgIf>
<NgIf cond={this.state.loading}>
<NgIf cond={this.state.error === false}>
<Loader/>
</NgIf>
<NgIf cond={this.state.error !== false} onClick={this.componentDidMount.bind(this)} style={{cursor: 'pointer'}}>
<Error err={this.state.error}/>
</NgIf>
<NgIf className="error" cond={this.state.error !== false} onClick={this.componentDidMount.bind(this)}>
<Error err={this.state.error}/>
</NgIf>
</div>
</NgIf>
</div>
</div>
);
}
}

View File

@ -0,0 +1,16 @@
.component_page_filespage{
.error{
cursor: pointer;
}
.container{
padding: 5px 0 20px 0;
height: 100%;
boxSizing: border-box;
}
}
.scroll-y{
overflow-y: auto!important;
overflow-x: hidden!important;
-webkit-overflow-scrolling: touch;
}

View File

@ -1,12 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { EventEmitter } from '../../data';
import { BreadCrumb, PathElement } from '../../components/breadcrumb';
import { pathBuilder } from '../../utilities';
import { DropTarget } from 'react-dnd';
import { EventEmitter, BreadCrumb, PathElement } from '../../components/';
import { pathBuilder } from '../../helpers/';
export default class BreadCrumbTargettable extends BreadCrumb{
export class BreadCrumbTargettable extends BreadCrumb{
constructor(props){
super(props);
}

View File

@ -1,9 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Card, NgIf, Icon, pathBuilder, theme } from '../../utilities';
import { EventEmitter } from '../../data';
import { DragSource, DropTarget } from 'react-dnd';
import './existingthing.scss';
import { Card, NgIf, Icon, EventEmitter} from '../../components/';
import { pathBuilder } from '../../helpers/';
const fileSource = {
beginDrag(props, monitor, component) {
return {
@ -24,15 +26,15 @@ const fileSource = {
component.setState({icon: 'loading', message: null}, function(){
props.emit.apply(component, ['file.rename'].concat(result.args))
.then((ok) => {
component.setState({appear: false})
component.setState({appear: false});
})
.catch(err => {
if(err && err.code === 'CANCELLED'){ return }
component.setState({icon: 'error', message: err.message})
})
if(err && err.code === 'CANCELLED'){ return; }
component.setState({icon: 'error', message: err.message});
});
});
}else{
throw 'unknown action'
throw 'unknown action';
}
}
}
@ -53,7 +55,7 @@ const fileTarget = {
let from = pathBuilder(props.path, src.name, src.type);
let to = pathBuilder(props.path, './'+dest.name+'/'+src.name, src.type);
return {action: 'rename', args: [from, to, src.type], ctx: 'existingfile'}
return {action: 'rename', args: [from, to, src.type], ctx: 'existingfile'};
}
};
@ -90,15 +92,16 @@ export class ExistingThing extends React.Component {
hover: null,
message: null,
icon: props.file.type,
filename: props.file.name
filename: props.file.name,
request_delete: false
};
}
componentWillReceiveProps(props){
this.setState({
filename: props.file.name,
message: props.file.message || null,
})
message: props.file.message || null
});
}
@ -106,7 +109,7 @@ export class ExistingThing extends React.Component {
if(this.state.icon !== 'loading'){
this.props.emit('file.select', pathBuilder(this.props.path, this.props.file.name, this.props.file.type), this.props.file.type)
.catch((err) => {
if(err && err.code === 'CANCELLED'){ return }
if(err && err.code === 'CANCELLED'){ return; }
this.setState({icon: 'error', message: err.message});
});
}
@ -123,14 +126,17 @@ export class ExistingThing extends React.Component {
)
.then((ok) => this.props.emit('file.refresh', this.props.path))
.catch((err) => {
if(err && err.code === 'CANCELLED'){ return }
if(err && err.code === 'CANCELLED'){ return; }
this.setState({icon: 'error', message: err.message, filename: oldFilename});
});
}
onDelete(filename){
onDeleteRequest(filename){
let toConfirm = this.props.file.name.length > 16? this.props.file.name.substring(0, 10).toLowerCase() : this.props.file.name;
let answer = prompt('Confirm by tapping "'+toConfirm+'"');
console.log(answer);
}
onDeleteConfirm(filename, toConfirm, answer){
if(answer === toConfirm){
this.setState({icon: 'loading'});
this.props.emit(
@ -138,9 +144,9 @@ export class ExistingThing extends React.Component {
pathBuilder(this.props.path, this.props.file.name),
this.props.file.type
).then((ok) => {
this.setState({appear: false})
this.setState({appear: false});
}).catch((err) => {
if(err && err.code === 'CANCELLED'){ return }
if(err && err.code === 'CANCELLED'){ return; }
this.setState({icon: 'error', message: err.message});
});
}
@ -153,14 +159,14 @@ export class ExistingThing extends React.Component {
if(this.props.isDragging) { dragStyle.opacity = 0.15; }
if(this.state.hover === true){
dragStyle.background = theme.effects.hover;
dragStyle.background = '#f5f5f5';
}
if((this.props.fileIsOver && this.props.canDropFile) || (this.props.nativeFileIsOver && this.props.canDropNativeFile)) {
dragStyle.background = theme.effects.selected;
dragStyle.background = '#c5e2f1';
}
return connectDragSource(connectDropNativeFile(connectDropFile(
<div>
<div className="component_existingthing">
<NgIf cond={this.state.appear}>
<Card onClick={this.onSelect.bind(this)} onMouseEnter={() => this.setState({hover: true})} onMouseLeave={() => this.setState({hover: false})} style={dragStyle}>
<DateTime show={this.state.hover !== true || this.state.icon === 'loading'} timestamp={this.props.file.time} background={dragStyle.background}/>
@ -171,16 +177,19 @@ export class ExistingThing extends React.Component {
background={dragStyle.background}
show={this.state.hover === true && this.state.icon !== 'loading' && !('ontouchstart' in window)}
onRename={this.onRename.bind(this)}
onDelete={this.onDelete.bind(this)} />
onDelete={this.onDeleteRequest.bind(this)} />
<FileSize type={this.props.file.type} size={this.props.file.size} />
<Message message={this.state.message} />
</Card>
</NgIf>
<NgIf cond={this.state.request_delete}>
</NgIf>
</div>
)));
}
}
// <Prompt message="" onCancel={() => {}} onConfirm={this.onDeleteConfirm.bind(this, this.state.filename, 'test')} />
ExistingThing.PropTypes = {
connectDragSource: PropTypes.func.isRequired,
isDragging: PropTypes.bool.isRequired,
@ -192,21 +201,21 @@ ExistingThing.PropTypes = {
class Updater extends React.Component {
constructor(props){
super(props)
super(props);
this.state = {
editing: null
}
};
}
onRename(e){
e.preventDefault();
this.props.onRename(this.state.editing);
this.setState({editing: null})
this.setState({editing: null});
}
onDelete(e){
e.stopPropagation();
this.props.onDelete()
this.props.onDelete();
}
@ -229,7 +238,7 @@ class Updater extends React.Component {
inline: {display: 'inline'},
el: {float: 'right', color: '#6f6f6f', height: '22px', background: this.props.background || 'white', margin: '0 -10px', padding: '0 10px', position: 'relative'},
margin: {marginRight: '10px'}
}
};
return (
<div style={{display: 'inline'}}>
<NgIf cond={this.props.show} style={style.el}>

View File

@ -0,0 +1,3 @@
.component_existingthing{
}

View File

@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import Path from 'path';
import { Container, NgIf } from '../utilities';
import { NewThing, ExistingThing, FileZone } from '../pages/filespage/';
import { DropTarget } from 'react-dnd';
import Path from 'path';
import { Container, NgIf } from '../../components/';
import { NewThing } from './newthing';
import { ExistingThing } from './existingthing';
import { FileZone } from './filezone';
@DropTarget('__NATIVE_FILE__', {}, (connect, monitor) => ({
connectDropFile: connect.dropTarget(),
@ -18,7 +19,7 @@ export class FileSystem extends React.Component {
creating: null,
access_right: this._findAccessRight(props.files),
sort: 'type'
}
};
}
_findAccessRight(files){

View File

@ -1,9 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { DropTarget } from 'react-dnd';
import { EventEmitter } from '../../data';
import { theme, to_rgba } from '../../utilities';
import { EventEmitter } from '../../components/';
@EventEmitter
@DropTarget('__NATIVE_FILE__', {
@ -29,8 +28,8 @@ export class FileZone extends React.Component{
fontWeight: 'bold'
}
if(this.props.fileIsOver){
style.background = to_rgba(theme.colors.primary, 0.5);
style.border = '2px dashed '+theme.colors.primary;
style.background = '#B4EBFF';
style.border = '2px dashed #9AD1ED';
style.color = 'white'
}
return this.props.connectDropFile(
@ -43,4 +42,3 @@ export class FileZone extends React.Component{
FileZone.PropTypes = {
path: PropTypes.string.isRequired
}

View File

@ -1,3 +1,2 @@
export { NewThing } from './newthing';
export { ExistingThing } from './existingthing';
export { FileZone } from './filezone';
export { FileSystem } from './filesystem.js';
export { BreadCrumbTargettable as BreadCrumb } from './breadcrumb';

View File

@ -1,8 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { EventEmitter } from '../../data';
import { Card, NgIf, Icon, pathBuilder } from '../../utilities';
import { Card, NgIf, Icon, EventEmitter } from '../../components/';
import { pathBuilder } from '../../helpers/';
@EventEmitter
export class NewThing extends React.Component {

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Session } from '../data';
import { Loader } from '../utilities';
import { Session } from '../model/';
import { Loader } from '../components/';
export class HomePage extends React.Component {
constructor(props){

View File

@ -2,6 +2,5 @@ export { HomePage } from './homepage';
export { ConnectPage } from './connectpage';
export { LogoutPage } from './logout';
export { NotFoundPage } from './notfound';
// Those are commented because they will delivered as a separate chunk
// export { FilesPage } from './filespage';
// export { ViewerPage } from './viewerpage';
export { FilesPage } from './filespage';
export { ViewerPage } from './viewerpage';

View File

@ -1,6 +1,8 @@
import React from 'react';
import { Session, invalidate } from '../data';
import { Loader } from '../utilities';
import { Session } from '../model/';
import { Loader } from '../components/';
import { invalidate } from '../helpers/';
export class LogoutPage extends React.Component {
constructor(props){

View File

@ -1,10 +1,10 @@
import React from 'react';
import { BreadCrumb } from '../components/';
import { Bundle, debounce, NgIf, Loader, Error, Container } from '../utilities/';
import { Files, opener, EventReceiver, EventEmitter } from '../data/';
import { AudioPlayer, FileDownloader, ImageViewer, PDFViewer } from './viewerpage/';
import Path from 'path';
import { Files } from '../model/';
import { BreadCrumb, Bundle, NgIf, Loader, Error, Container, EventReceiver, EventEmitter } from '../components/';
import { debounce, opener } from '../helpers/';
import { AudioPlayer, FileDownloader, ImageViewer, PDFViewer } from './viewerpage/';
const VideoPlayer = (props) => (
<Bundle loader={import(/* webpackChunkName: "video" */"../pages/viewerpage/videoplayer")} symbol="VideoPlayer">
@ -17,7 +17,6 @@ const IDE = (props) => (
</Bundle>
);
@EventReceiver
export class ViewerPage extends React.Component {
constructor(props){
@ -66,35 +65,35 @@ export class ViewerPage extends React.Component {
}
componentWillUnmount() {
this.props.unsubscribe('file.select')
this.props.unsubscribe('file.select');
window.removeEventListener("resize", this.resetHeight);
}
save(file){
this.setState({isSaving: true})
this.setState({isSaving: true});
Files.save(this.state.path, file)
.then(() => {
this.setState({isSaving: false})
this.setState({needSaving: false})
this.setState({isSaving: false});
this.setState({needSaving: false});
})
.catch((err) => {
if(err && err.code === 'CANCELLED'){ return }
this.setState({isSaving: false})
let message = "Oups, something went wrong"
if(err && err.code === 'CANCELLED'){ return; }
this.setState({isSaving: false});
let message = "Oups, something went wrong";
if(err.message){
message += ':\n'+err.message
message += ':\n'+err.message;
}
alert(message);
});
}
onPathUpdate(path){
this.props.history.push('/files'+path)
this.props.history.push('/files'+path);
}
needSaving(bool){
this.setState({needSaving: bool})
this.setState({needSaving: bool});
}
componentDidMount(){

View File

@ -1,8 +1,9 @@
import React from 'react';
import { MenuBar } from './menubar';
import { NgIf, Icon, theme } from '../../utilities/'
import WaveSurfer from 'wavesurfer.js';
import { MenuBar } from './menubar';
import { NgIf, Icon } from '../../components/'
export class AudioPlayer extends React.Component {
constructor(props){
super(props);
@ -83,7 +84,7 @@ export class AudioPlayer extends React.Component {
<NgIf cond={this.state.loading === true}>
<Icon name="loading" />
</NgIf>
<div style={{background: '#f1f1f1', boxShadow: theme.effects.shadow, opacity: this.state.loading? '0' : '1', position: 'relative'}}>
<div style={{background: '#f1f1f1', boxShadow: 'rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px', opacity: this.state.loading? '0' : '1', position: 'relative'}}>
<div style={{position: 'absolute', top: '10px', right: '10px', zIndex: '2', height: '30px'}}>
<NgIf cond={this.state.isPlaying === false} style={{display: 'inline'}}>
<span style={{cursor: 'pointer'}} onClick={this.onPlay.bind(this)}><Icon name="play"/></span>

View File

@ -2,6 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import CodeMirror from 'codemirror/lib/codemirror';
import 'codemirror/lib/codemirror.css';
import './editor.scss';
window.CodeMirror = CodeMirror;
// keybinding
@ -17,7 +19,7 @@ import 'codemirror/addon/dialog/dialog.js';
// code folding
import 'codemirror/addon/fold/foldcode';
import 'codemirror/addon/fold/foldgutter';
import 'codemirror/addon/fold/foldgutter.css';
export class Editor extends React.Component {
constructor(props){
@ -108,7 +110,7 @@ export class Editor extends React.Component {
else if(ext === 'markdown' || ext === 'md'){mode = 'markdown'; }
else if(ext === 'pl' || ext === 'pm'){mode = 'perl'; }
else if(ext === 'clj'){ mode = 'clojure'; }
else if(ext === 'el' || ext === 'lisp' || ext === 'cl'){ mode = 'lisp'; }
else if(ext === 'el' || ext === 'lisp' || ext === 'cl' || ext === 'emacs'){ mode = 'lisp'; }
else if(ext === 'Dockerfile'){ mode = 'dockerfile'}
else if(ext === 'R'){ mode = 'r'; }
else if(ext === 'Makefile'){ mode = 'cmake'; }
@ -126,7 +128,7 @@ export class Editor extends React.Component {
mode = 'text';
}
return import(/* webpackChunkName: "editor" */'../pages/viewerpage/editor/'+mode)
return import(/* webpackChunkName: "editor" */'./editor/'+mode)
.then((module) => Promise.resolve(module.default));
}
@ -142,4 +144,4 @@ Editor.propTypes = {
filename: PropTypes.string.isRequired,
onChange: PropTypes.func,
onSave: PropTypes.func
}
};

View File

@ -0,0 +1,57 @@
.CodeMirror {
height: 100%;
color: #333333;
}
/* SEARCH */
.CodeMirror-dialog {
position: fixed;
left: 0; right: 0;
background: #525659;
z-index: 15;
padding: 5px .8em;
overflow: hidden;
color: #e2e2e2;
box-shadow: 2px 2px 2px rgba(0,0,0,0.5)
}
.CodeMirror-dialog-top {
border-bottom: 1px solid #eee;
bottom: 0;
}
.CodeMirror-dialog-bottom {
border-top: 1px solid #eee;
bottom: 0;
}
.CodeMirror-dialog input {
border: none;
outline: none;
background: transparent;
width: 20em;
color: white;
font-family: monospace;
}
.CodeMirror-dialog button {
font-size: 70%;
}
.CodeMirror-scroll { -webkit-overflow-scrolling: touch; }
.cm-s-default .cm-header {color: #3E7AA6; font-size: 1.15em;}
.cm-s-default .cm-header.cm-org-level-star{color: #6f6f6f; position: relative; top: 2px;}
.cm-s-default .cm-header.cm-org-todo{color: #FF8355;}
.cm-s-default .cm-header.cm-org-done{color: #3BB27C;}
.cm-s-default .cm-link{color: #555!important;}
.cm-s-default .cm-url{color: #555!important;}
.cm-s-default .cm-keyword {color: #3E7AA6;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #6f6f6f;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}

View File

@ -1,6 +1,7 @@
import React from 'react';
import { MenuBar } from './menubar';
import { theme, NgIf, Icon } from '../../utilities/'
import { NgIf, Icon } from '../../components/';
export class FileDownloader extends React.Component{
constructor(props){
@ -27,7 +28,7 @@ export class FileDownloader extends React.Component{
render(){
return (
<div style={{textAlign: 'center', background: '#525659', height: '100%'}}>
<div style={{padding: '15px 20px', background: '#323639', borderRadius: '2px', color: 'inherit', boxShadow: theme.effects.shadow, display: 'inline-block', marginTop: '50px'}}>
<div style={{padding: '15px 20px', background: '#323639', borderRadius: '2px', color: 'inherit', boxShadow: 'rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px', display: 'inline-block', marginTop: '50px'}}>
<a download={this.props.filename} href={this.props.data}>
<NgIf onClick={this.onClick.bind(this)} cond={!this.state.loading} style={{fontSize: '17px', display: 'inline-block'}}>
DOWNLOAD

View File

@ -1,20 +1,20 @@
import React from 'react';
import { Editor } from '../../components/editor';
import { NgIf, Fab, Icon } from '../../utilities/'
import { MenuBar } from './menubar';
import { NgIf, Fab, Icon } from '../../components/';
import { MenuBar } from './menubar';
import { Editor } from './editor';
export class IDE extends React.Component {
constructor(props){
super(props);
this.state = {
contentToSave: null
}
};
}
onContentUpdate(text){
this.props.needSaving(true);
this.setState({contentToSave: text})
this.setState({contentToSave: text});
}
save(){

View File

@ -1,5 +1,5 @@
import React from 'react';
import { theme } from '../../utilities/';
import { MenuBar } from './menubar';
export const ImageViewer = (props) => {
@ -7,7 +7,7 @@ export const ImageViewer = (props) => {
<div style={{height: '100%'}}>
<MenuBar title={props.filename} download={props.data} />
<div style={{textAlign: 'center', background: '#525659', height: 'calc(100% - 34px)', overflow: 'hidden', padding: '20px', boxSizing: 'border-box'}}>
<img src={props.data} style={{maxHeight: '100%', maxWidth: '100%', minHeight: '100px', background: '#f1f1f1', boxShadow: theme.effects.shadow}} />
<img src={props.data} style={{maxHeight: '100%', maxWidth: '100%', minHeight: '100px', background: '#f1f1f1', boxShadow: 'rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px'}} />
</div>
</div>
)

View File

@ -2,6 +2,6 @@ export { AudioPlayer } from './audioplayer';
export { FileDownloader } from './filedownloader';
export { ImageViewer } from './imageviewer';
export { PDFViewer } from './pdfviewer';
// Those are commented because they will delivered as a separate chunk
// export { VideoPlayer } from './videoplayer';
// export { IDE } from './ide';
// Those are commented because they will be delivered as a separate chunk
//export { VideoPlayer } from './videoplayer';
//export { IDE } from './ide';

View File

@ -1,5 +1,6 @@
import React from 'react';
import { theme, Container, NgIf, Icon } from '../../utilities/';
import { Container, NgIf, Icon } from '../../components/';
export class MenuBar extends React.Component{
constructor(props){
@ -26,7 +27,7 @@ export class MenuBar extends React.Component{
render(){
return (
<div style={{background: '#313538', color: '#f1f1f1', boxShadow: theme.effects.shadow_small}}>
<div style={{background: '#313538', color: '#f1f1f1', boxShadow: 'rgba(0, 0, 0, 0.14) 2px 2px 2px 0px'}}>
<Container style={{padding: '9px 0', textAlign: 'center', color: '#f1f1f1', fontSize: '0.9em'}}>
<NgIf cond={this.props.hasOwnProperty('download')} style={{float: 'right', height: '1em'}}>
<NgIf cond={!this.state.loading} style={{display: 'inline'}}>

View File

@ -1,7 +1,7 @@
import React from 'react';
import { MenuBar } from './menubar';
import { theme } from '../../utilities/';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';
export class VideoPlayer extends React.Component {
constructor(props){
@ -31,7 +31,7 @@ export class VideoPlayer extends React.Component {
<MenuBar title={this.props.filename} download={this.props.data} />
<div style={{padding: '20px'}}>
<div style={{maxWidth: '800px', width: '100%', margin: '0 auto'}}>
<video ref={ node => this.videoNode = node } className="video-js my-skin" style={{boxShadow: theme.effects.shadow}}></video>
<video ref={ node => this.videoNode = node } className="video-js my-skin" style={{boxShadow: 'rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px'}}></video>
</div>
</div>
</div>

View File

@ -1,29 +1,34 @@
import React from 'react'
import { BrowserRouter, Route, IndexRoute } from 'react-router-dom'
import { NotFoundPage, ConnectPage, HomePage, LogoutPage } from './pages/';
import { Bundle, URL_HOME, URL_FILES, URL_VIEWER, URL_LOGIN, URL_LOGOUT } from './utilities/';
import React from 'react';
import { BrowserRouter, Route, IndexRoute, Switch } from 'react-router-dom';
import { NotFoundPage, ConnectPage, HomePage, LogoutPage, FilesPage, ViewerPage } from './pages/';
import { Bundle, URL_HOME, URL_FILES, URL_VIEWER, URL_LOGIN, URL_LOGOUT } from './helpers/';
const FilesPage = (props) => (
<Bundle loader={import(/* webpackChunkName: "route" */ "./pages/filespage")} symbol="FilesPage">
{(Comp) => <Comp {...props}/>}
</Bundle>
);
const ViewerPage = (props) => (
<Bundle loader={import(/* webpackChunkName: "route" */"./pages/viewerpage")} symbol="ViewerPage">
{(Comp) => <Comp {...props}/>}
</Bundle>
);
// import {FilesPage} from './pages/filespage';
// import {ViewerPage} from './pages/viewerpage';
// const FilesPage = (props) => (
// <Bundle loader={import(/* webpackChunkName: "route" */ "./pages/filespage")} symbol="FilesPage">
// {(Comp) => <Comp {...props}/>}
// </Bundle>
// );
// const ViewerPage = (props) => (
// <Bundle loader={import(/* webpackChunkName: "route" */"./pages/viewerpage")} symbol="ViewerPage">
// {(Comp) => <Comp {...props}/>}
// </Bundle>
// );
export default class AppRouter extends React.Component {
render() {
return (
<BrowserRouter>
<div>
<Route exact path="/" component={HomePage} />
<Route path="/login" component={ConnectPage} />
<Route path="/files/:path*" component={FilesPage} />
<Route path="/view/:path*" component={ViewerPage} />
<Route path="/logout" component={LogoutPage} />
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/login" component={ConnectPage} />
<Route path="/files/:path*" component={FilesPage} />
<Route path="/view/:path*" component={ViewerPage} />
<Route path="/logout" component={LogoutPage} />
<Route component={NotFoundPage} />
</Switch>
</div>
</BrowserRouter>
);

Some files were not shown because too many files have changed in this diff Show More