diff --git a/client/data/tools.js b/client/data/tools.js
index 229cd0ff..562a4250 100644
--- a/client/data/tools.js
+++ b/client/data/tools.js
@@ -54,14 +54,18 @@ export function http_get(url, cache_expire = 0, type = 'json'){
}
}else{
done(xhr.responseText);
- }
+ }
}else{
- err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
+ if(navigator.onLine === false){
+ err({status: xhr.status, code: "CONNECTION_LOST", message: 'Connection Lost'});
+ }else{
+ err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
+ }
}
}
}
xhr.open('GET', url, true);
- xhr.send(null);
+ xhr.send(null);
});
}
}
@@ -78,7 +82,7 @@ export function http_post(url, data, type = 'json'){
}
xhr.send(data);
xhr.onload = function () {
- if (xhr.readyState === XMLHttpRequest.DONE) {
+ if (xhr.readyState === XMLHttpRequest.DONE) {
if(xhr.status === 200){
try{
let data = JSON.parse(xhr.responseText);
@@ -91,11 +95,15 @@ export function http_post(url, data, type = 'json'){
}
}catch(error){
err({message: 'oups', trace: error});
- }
+ }
}else{
- err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
+ if(navigator.onLine === false){
+ err({status: xhr.status, code: "CONNECTION_LOST", message: 'Connection Lost'});
+ }else{
+ err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
+ }
}
- }
+ }
}
});
}
@@ -106,7 +114,7 @@ export function http_delete(url){
xhr.open("DELETE", url, true);
xhr.withCredentials = true;
xhr.onload = function () {
- if (xhr.readyState === XMLHttpRequest.DONE) {
+ if (xhr.readyState === XMLHttpRequest.DONE) {
if(xhr.status === 200){
try{
let data = JSON.parse(xhr.responseText);
@@ -119,11 +127,15 @@ export function http_delete(url){
}
}catch(error){
err({message: 'oups', trace: error});
- }
+ }
}else{
- err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
+ if(navigator.onLine === false){
+ err({status: xhr.status, code: "CONNECTION_LOST", message: 'Connection Lost'});
+ }else{
+ err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
+ }
}
- }
+ }
}
xhr.send(null);
});
diff --git a/client/index.html b/client/index.html
index 8b476b15..bd0dd5bf 100644
--- a/client/index.html
+++ b/client/index.html
@@ -1,23 +1,42 @@
-
-
-
-
-
-
-
-
-
- Nuage
-
-
-
-
-
-
-
-
-
-
+
+
+ Nuage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/index.js b/client/index.js
index 52f063f0..dc491f05 100644
--- a/client/index.js
+++ b/client/index.js
@@ -2,6 +2,13 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Router from './router';
+if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.register('/cache.js').then(function(registration) {
+ }).catch(function(error) {
+ console.log('ServiceWorker registration failed:', error);
+ });
+}
+
window.onload = () => {
ReactDOM.render(, document.getElementById('main'));
};
diff --git a/client/pages/filespage.js b/client/pages/filespage.js
index b55b78a4..04c6b502 100644
--- a/client/pages/filespage.js
+++ b/client/pages/filespage.js
@@ -12,7 +12,7 @@ import Path from 'path';
@EventReceiver
@DragDropContext(('ontouchstart' in window)? HTML5Backend : HTML5Backend)
-export class FilesPage extends React.Component {
+export class FilesPage extends React.Component {
constructor(props){
super(props);
this.state = {
@@ -37,6 +37,7 @@ export class FilesPage extends React.Component {
componentWillMount(){
+ this.setState({error: false});
this.onPathUpdate(this.state.path, 'directory', true)
}
@@ -58,9 +59,13 @@ export class FilesPage extends React.Component {
onRefresh(path = this.state.path){
+ this.setState({error: false})
return Files.ls(path).then((files) => {
this.setState({files: files, loading: false})
- });
+ }).catch((error) => {
+ this.setState({error: error});
+ })
+
}
onPathUpdate(path, type = 'directory', withLoader = true){
@@ -104,7 +109,7 @@ export class FilesPage extends React.Component {
icon: 'loading',
virtual: true
}
- });
+ });
const files = JSON.parse(JSON.stringify(this.state.files));
this.setState({files: [].concat(newfiles, files)});
return Promise.resolve(_files);
@@ -150,7 +155,7 @@ export class FilesPage extends React.Component {
}
};
}
-
+
function job(it){
let file = it.next();
if(file){
@@ -168,12 +173,12 @@ export class FilesPage extends React.Component {
return job(it);
}));
}
-
+
const poolSize = 10;
return createFilesInUI(files)
.then((files) => Promise.resolve(generator(files)))
.then((it) => process(it, poolSize))
- .then((res) => Promise.resolve('ok'));
+ .then((res) => Promise.resolve('ok'));
}
@@ -183,9 +188,9 @@ export class FilesPage extends React.Component {
});
}
-
+
render() {
- return (
+ return (
@@ -196,8 +201,8 @@ export class FilesPage extends React.Component {
-
-
+
+
@@ -206,6 +211,3 @@ export class FilesPage extends React.Component {
);
}
}
-
-
-
diff --git a/client/pages/viewerpage.js b/client/pages/viewerpage.js
index cbb766db..0df0ceec 100644
--- a/client/pages/viewerpage.js
+++ b/client/pages/viewerpage.js
@@ -20,7 +20,7 @@ const IDE = (props) => (
@EventReceiver
export class ViewerPage extends React.Component {
- constructor(props){
+ constructor(props){
super(props);
this.state = {
path: props.match.url.replace('/view', ''),
@@ -37,6 +37,34 @@ export class ViewerPage extends React.Component {
this.props.subscribe('file.select', this.onPathUpdate.bind(this));
}
+ componentWillMount(){
+ this.setState({loading: true, error: false});
+ let app = opener(this.state.path);
+ if(app === 'editor'){
+ Files.cat(this.state.path).then((content) => {
+ this.setState({data: content, loading: false, opener: app});
+ }).catch(err => {
+ if(err && err.code === 'CANCELLED'){ return }
+ if(err.code === 'BINARY_FILE'){
+ Files.url(this.state.path).then((url) => {
+ this.setState({data: url, loading: false, opener: 'download'});
+ }).catch(err => {
+ this.setState({error: err});
+ });
+ }else{
+ this.setState({error: err});
+ }
+ });
+ }else{
+ Files.url(this.state.path).then((url) => {
+ this.setState({data: url, loading: false, opener: app});
+ }).catch(err => {
+ if(err && err.code === 'CANCELLED'){ return }
+ this.setState({error: err});
+ });
+ }
+ }
+
componentWillUnmount() {
this.props.unsubscribe('file.select')
window.removeEventListener("resize", this.resetHeight);
@@ -72,32 +100,6 @@ export class ViewerPage extends React.Component {
componentDidMount(){
this.resetHeight();
window.addEventListener("resize", this.resetHeight);
- this.setState({loading: true});
- let app = opener(this.state.path);
- if(app === 'editor'){
- Files.cat(this.state.path).then((content) => {
- this.setState({data: content, loading: false, opener: app});
- }).catch(err => {
- if(err && err.code === 'CANCELLED'){ return }
- if(err.code === 'BINARY_FILE'){
- Files.url(this.state.path).then((url) => {
- this.setState({data: url, loading: false, opener: 'download'});
- }).catch(err => {
- this.setState({error: err});
- });
- }else{
- this.setState({error: err});
- }
- });
- }else{
- Files.url(this.state.path).then((url) => {
- this.setState({data: url, loading: false, opener: app});
- }).catch(err => {
- console.log("ERROR", err)
- if(err && err.code === 'CANCELLED'){ return }
- this.setState({error: err});
- });
- }
}
resetHeight(){
@@ -140,13 +142,12 @@ export class ViewerPage extends React.Component {
-
+
-
+
);
}
}
-
diff --git a/server/public/cache.js b/server/public/cache.js
new file mode 100644
index 00000000..1202151b
--- /dev/null
+++ b/server/public/cache.js
@@ -0,0 +1,160 @@
+const CACHE_NAME = 'v0.0';
+const URLS_TO_CACHE = ['/', '/index.html'];
+
+self.addEventListener('fetch', function(event){
+ if(is_a_ressource(event.request)){
+ //console.log("> FETCH RESSOURCE", event.request.url)
+ return fetchRessource(event);
+ }else if(is_an_api_call(event.request)){
+ //console.log("> FETCH API", event.request.url)
+ return fetchApi(event);
+ }else if(is_an_index(event.request)){
+ //console.log("> FETCH INDEX", event.request.url)
+ return fetchIndex(event);
+ }else{
+ //console.log("> FETCH FALLBACK", event.request.url)
+ return cacheFallback(event);
+ }
+});
+
+self.addEventListener('activate', function(event){
+ //console.log("> ACTIVATE")
+ vacuum(event)
+});
+self.addEventListener('install', function(event){
+ //console.log("> INSTALL SERVICE WORKER", navigator)
+ if (self.skipWaiting) { self.skipWaiting(); }
+})
+
+////////////////////////////////////////
+// ASSETS AND RESSOURCES
+////////////////////////////////////////
+
+function is_a_ressource(request){
+ return ['css', 'js', 'img', 'logo', 'manifest.json', 'favicon.ico'].indexOf(pathname(request)[0]) >= 0 ? true : false;
+}
+
+/*
+ * cache agressively but refresh the cache if possible
+ */
+function fetchRessource(event){
+ event.respondWith(
+ caches.open(CACHE_NAME).then(function(cache){
+ return cache.match(event.request)
+ .then(function(response){
+ if(response){
+ fetchAndCache(event).catch(nil)
+ return response;
+ }else{
+ return Promise.reject("OUPS");
+ }
+ })
+ .catch(function(err){
+ return fetchAndCache(event);
+ });
+ })
+ );
+
+ function fetchAndCache(event){
+ // A request is a stream and can only be consumed once. Since we are consuming this
+ // once by cache and once by the browser for fetch, we need to clone the response as
+ // seen on https://developers.google.com/web/fundamentals/getting-started/primers/service-workers
+ const request = event.request.clone();
+
+ return fetch(request)
+ .then(function(response){
+ if(!response){ return response; }
+
+ // A response is a stream and can only because we want the browser to consume the
+ // response as well as the cache consuming the response, we need to clone it
+ const responseToCache = response.clone();
+ caches.open(CACHE_NAME).then(function(cache){
+ cache.put(event.request, responseToCache);
+ });
+ return response;
+ });
+ }
+}
+
+
+
+////////////////////////////////////////
+// API CALL
+////////////////////////////////////////
+function is_an_api_call(request){
+ return pathname(request)[0] === 'api' ? true : false;
+}
+
+function fetchApi(event){
+}
+
+
+
+////////////////////////////////////////
+// INDEX CALL
+////////////////////////////////////////
+function is_an_index(request){
+ return ['login', 'files', 'view', 'logout'].indexOf(pathname(request)[0]) >= 0? true : false;
+}
+function fetchIndex(event){
+ event.request.url = host(event.request);
+ event.respondWith(
+ caches.open(CACHE_NAME).then(function(cache){
+ return cache.match('/').then(function(response){
+ return response || fetch('/').then(function(response) {
+ if(response && response.status === 200){
+ cache.put('/', response.clone());
+ }
+ return response;
+ })
+ })
+ })
+ )
+}
+
+
+////////////////////////////////////////
+// OTHER STUFF
+////////////////////////////////////////
+function cacheFallback(event){
+ event.respondWith(
+ caches.open(CACHE_NAME).then(function(cache){
+ return cache.match(event.request).then(function(response){
+ if(response){
+ return response;
+ }else{
+ return fetch(event.request.clone())
+ .then(function(response){
+ cache.put(event.request, response.clone())
+ return response;
+ });
+ }
+ });
+ })
+ )
+}
+
+
+////////////////////////////////////////
+// HELPERS
+////////////////////////////////////////
+
+function vacuum(event){
+ return event.waitUntil(
+ caches.keys().then(function(cachesName){
+ return Promise.all(cachesName.map(function(cacheName){
+ if(cacheName !== CACHE_NAME){
+ return caches.delete(cacheName);
+ }
+ }));
+ })
+ );
+}
+
+function host(request){
+ return request.url.replace(/(http[s]?\:\/\/[^\/]*\/).*/, '$1');
+}
+function pathname(request){
+ return request.url.replace(/^http[s]?:\/\/[^\/]*\//, '').split('/')
+}
+function nil(e){}
diff --git a/server/public/img/logo.png b/server/public/img/logo.png
index ac840fc1..6c05a3a6 100644
Binary files a/server/public/img/logo.png and b/server/public/img/logo.png differ
diff --git a/server/public/logo/android-icon-144x144.png b/server/public/logo/android-icon-144x144.png
new file mode 100644
index 00000000..7033af94
Binary files /dev/null and b/server/public/logo/android-icon-144x144.png differ
diff --git a/server/public/logo/android-icon-192x192.png b/server/public/logo/android-icon-192x192.png
new file mode 100644
index 00000000..f8ae45c0
Binary files /dev/null and b/server/public/logo/android-icon-192x192.png differ
diff --git a/server/public/logo/android-icon-36x36.png b/server/public/logo/android-icon-36x36.png
new file mode 100644
index 00000000..d6d0b1a7
Binary files /dev/null and b/server/public/logo/android-icon-36x36.png differ
diff --git a/server/public/logo/android-icon-48x48.png b/server/public/logo/android-icon-48x48.png
new file mode 100644
index 00000000..14041fdb
Binary files /dev/null and b/server/public/logo/android-icon-48x48.png differ
diff --git a/server/public/logo/android-icon-72x72.png b/server/public/logo/android-icon-72x72.png
new file mode 100644
index 00000000..27b820c7
Binary files /dev/null and b/server/public/logo/android-icon-72x72.png differ
diff --git a/server/public/logo/android-icon-96x96.png b/server/public/logo/android-icon-96x96.png
new file mode 100644
index 00000000..ef5fc4db
Binary files /dev/null and b/server/public/logo/android-icon-96x96.png differ
diff --git a/server/public/logo/apple-icon-114x114.png b/server/public/logo/apple-icon-114x114.png
new file mode 100644
index 00000000..3e426a95
Binary files /dev/null and b/server/public/logo/apple-icon-114x114.png differ
diff --git a/server/public/logo/apple-icon-120x120.png b/server/public/logo/apple-icon-120x120.png
new file mode 100644
index 00000000..ea07bc34
Binary files /dev/null and b/server/public/logo/apple-icon-120x120.png differ
diff --git a/server/public/logo/apple-icon-144x144.png b/server/public/logo/apple-icon-144x144.png
new file mode 100644
index 00000000..7033af94
Binary files /dev/null and b/server/public/logo/apple-icon-144x144.png differ
diff --git a/server/public/logo/apple-icon-152x152.png b/server/public/logo/apple-icon-152x152.png
new file mode 100644
index 00000000..f3421d97
Binary files /dev/null and b/server/public/logo/apple-icon-152x152.png differ
diff --git a/server/public/logo/apple-icon-180x180.png b/server/public/logo/apple-icon-180x180.png
new file mode 100644
index 00000000..4d560e2b
Binary files /dev/null and b/server/public/logo/apple-icon-180x180.png differ
diff --git a/server/public/logo/apple-icon-57x57.png b/server/public/logo/apple-icon-57x57.png
new file mode 100644
index 00000000..8184032e
Binary files /dev/null and b/server/public/logo/apple-icon-57x57.png differ
diff --git a/server/public/logo/apple-icon-60x60.png b/server/public/logo/apple-icon-60x60.png
new file mode 100644
index 00000000..059d0130
Binary files /dev/null and b/server/public/logo/apple-icon-60x60.png differ
diff --git a/server/public/logo/apple-icon-72x72.png b/server/public/logo/apple-icon-72x72.png
new file mode 100644
index 00000000..27b820c7
Binary files /dev/null and b/server/public/logo/apple-icon-72x72.png differ
diff --git a/server/public/logo/apple-icon-76x76.png b/server/public/logo/apple-icon-76x76.png
new file mode 100644
index 00000000..2ffe65db
Binary files /dev/null and b/server/public/logo/apple-icon-76x76.png differ
diff --git a/server/public/logo/apple-icon-precomposed.png b/server/public/logo/apple-icon-precomposed.png
new file mode 100644
index 00000000..f16a581e
Binary files /dev/null and b/server/public/logo/apple-icon-precomposed.png differ
diff --git a/server/public/logo/apple-icon.png b/server/public/logo/apple-icon.png
new file mode 100644
index 00000000..f16a581e
Binary files /dev/null and b/server/public/logo/apple-icon.png differ
diff --git a/server/public/logo/favicon-16x16.png b/server/public/logo/favicon-16x16.png
new file mode 100644
index 00000000..50d12784
Binary files /dev/null and b/server/public/logo/favicon-16x16.png differ
diff --git a/server/public/logo/favicon-32x32.png b/server/public/logo/favicon-32x32.png
new file mode 100644
index 00000000..46fb6ecc
Binary files /dev/null and b/server/public/logo/favicon-32x32.png differ
diff --git a/server/public/logo/favicon-96x96.png b/server/public/logo/favicon-96x96.png
new file mode 100644
index 00000000..78f9c963
Binary files /dev/null and b/server/public/logo/favicon-96x96.png differ
diff --git a/server/public/logo/favicon.ico b/server/public/logo/favicon.ico
new file mode 100644
index 00000000..70996732
Binary files /dev/null and b/server/public/logo/favicon.ico differ
diff --git a/server/public/logo/logo_large.png b/server/public/logo/logo_large.png
new file mode 100644
index 00000000..36cbb5d5
Binary files /dev/null and b/server/public/logo/logo_large.png differ
diff --git a/server/public/logo/ms-icon-144x144.png b/server/public/logo/ms-icon-144x144.png
new file mode 100644
index 00000000..7033af94
Binary files /dev/null and b/server/public/logo/ms-icon-144x144.png differ
diff --git a/server/public/logo/ms-icon-150x150.png b/server/public/logo/ms-icon-150x150.png
new file mode 100644
index 00000000..be78794c
Binary files /dev/null and b/server/public/logo/ms-icon-150x150.png differ
diff --git a/server/public/logo/ms-icon-310x310.png b/server/public/logo/ms-icon-310x310.png
new file mode 100644
index 00000000..607c109e
Binary files /dev/null and b/server/public/logo/ms-icon-310x310.png differ
diff --git a/server/public/logo/ms-icon-70x70.png b/server/public/logo/ms-icon-70x70.png
new file mode 100644
index 00000000..37d33a0c
Binary files /dev/null and b/server/public/logo/ms-icon-70x70.png differ
diff --git a/server/public/manifest.json b/server/public/manifest.json
new file mode 100644
index 00000000..ac5d993c
--- /dev/null
+++ b/server/public/manifest.json
@@ -0,0 +1,47 @@
+{
+ "name": "Manage your files in the cloud with Nuage",
+ "short_name": "Nuage",
+ "icons": [
+ {
+ "src": "logo/android-icon-36x36.png",
+ "sizes": "36x36",
+ "type": "image\/png",
+ "density": "0.75"
+ },
+ {
+ "src": "logo/android-icon-48x48.png",
+ "sizes": "48x48",
+ "type": "image\/png",
+ "density": "1.0"
+ },
+ {
+ "src": "logo/android-icon-72x72.png",
+ "sizes": "72x72",
+ "type": "image\/png",
+ "density": "1.5"
+ },
+ {
+ "src": "logo/android-icon-96x96.png",
+ "sizes": "96x96",
+ "type": "image\/png",
+ "density": "2.0"
+ },
+ {
+ "src": "logo/android-icon-144x144.png",
+ "sizes": "144x144",
+ "type": "image\/png",
+ "density": "3.0"
+ },
+ {
+ "src": "logo/android-icon-192x192.png",
+ "sizes": "192x192",
+ "type": "image\/png",
+ "density": "4.0"
+ }
+ ],
+ "theme_color": "#9AD1ED",
+ "background_color": "#f2f2f2",
+ "orientation": "any",
+ "display": "standalone",
+ "start_url": "/"
+}