mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-13 00:19:30 -05:00
webui:
- added missing fclose - fixed makefile - added support for urls and forward/backward browsing - started optimization for touch screens git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@8123 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
parent
059a0b3bf7
commit
c5c524862c
@ -520,6 +520,7 @@ int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
|
||||
struct stat s;
|
||||
if(fstat(fileno(fd), &s) == -1)
|
||||
{
|
||||
fclose(fd);
|
||||
const char *error = "<html><body><p>Error: file was opened but stat failed.</p></body></html>";
|
||||
struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1);
|
||||
MHD_add_response_header(resp, "Content-Type", "text/html");
|
||||
|
@ -3,7 +3,7 @@ module.exports = function(grunt) {
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
watch: {
|
||||
// important: exclude node_modules
|
||||
files: ['**','!**/node_modules/**'],
|
||||
files: ['dist/**','!**/node_modules/**'],
|
||||
options: {
|
||||
livereload: true,
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ CSS = green-black.css
|
||||
all: dist $(JSEXTLIBS) $(addprefix dist/, $(JSLIBS)) $(addprefix dist/, $(HTML)) $(addprefix dist/, $(JSGUI)) $(addprefix dist/, $(CSS))
|
||||
.PHONY: all
|
||||
|
||||
dist/react.js: dist
|
||||
dist/react.js:
|
||||
cd dist && wget --no-check-certificate --output-document react.js http://fb.me/react-$(REACT_VERSION).js
|
||||
|
||||
dist/JSXTransformer.js: dist
|
||||
dist/JSXTransformer.js:
|
||||
cd dist && wget --no-check-certificate --output-document JSXTransformer.js http://fb.me/JSXTransformer-$(REACT_VERSION).js
|
||||
|
||||
$(addprefix dist/, $(JSLIBS)): dist/%: %
|
||||
|
@ -13,7 +13,38 @@ if(require.main === module)
|
||||
var RS = new RsApi(connection);
|
||||
|
||||
var tests = [];
|
||||
PeersTest(tests);
|
||||
var doc = {
|
||||
counter: 0,
|
||||
toc: [],
|
||||
content: [],
|
||||
header: function(h){
|
||||
this.toc.push(h);
|
||||
this.content.push("<a name=\""+this.counter+"\"><h1>"+h+"</h1></a>");
|
||||
this.counter += 1;
|
||||
},
|
||||
paragraph: function(p){
|
||||
this.content.push("<p>"+p+"</p>");
|
||||
},
|
||||
|
||||
};
|
||||
PeersTest(tests, doc);
|
||||
|
||||
var docstr = "<!DOCTYPE html><html><body>";
|
||||
docstr += "<h1>Table of Contents</h1>";
|
||||
docstr += "<ul>";
|
||||
for(var i in doc.toc)
|
||||
{
|
||||
docstr += "<li><a href=\"#"+i+"\">"+doc.toc[i]+"</a></li>";
|
||||
}
|
||||
docstr += "</ul>";
|
||||
for(var i in doc.content)
|
||||
{
|
||||
docstr += doc.content[i];
|
||||
}
|
||||
docstr += "</body></html>";
|
||||
|
||||
var fs = require('fs');
|
||||
fs.writeFile("dist/api_documentation.html", docstr);
|
||||
|
||||
tests.map(function(test){
|
||||
test(RS);
|
||||
@ -39,6 +70,9 @@ function PeersTest(tests, doc)
|
||||
});
|
||||
var peers_list = new Type("peers_list",[peer_info]);
|
||||
|
||||
doc.header("peers");
|
||||
doc.paragraph("<pre>"+graphToText(peers_list)+"</pre>");
|
||||
|
||||
tests.push(function(RS){
|
||||
console.log("testing peers module...");
|
||||
console.log("expected schema is:")
|
||||
@ -106,126 +140,3 @@ function PeersTest(tests, doc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ************ below is OLD stuff, to be removed ************
|
||||
var Location =
|
||||
{
|
||||
avatar_address: String(),
|
||||
groups: undefined,
|
||||
is_online: Boolean(),
|
||||
location: String(),
|
||||
name: String(),
|
||||
peer_id: undefined,
|
||||
pgp_id: undefined,
|
||||
};
|
||||
var PeerInfo =
|
||||
{
|
||||
name: String(),
|
||||
locations: [Location],
|
||||
};
|
||||
var PeersList = [PeerInfo];
|
||||
|
||||
function checkIfMatch(ref, other)
|
||||
{
|
||||
var ok = true;
|
||||
|
||||
// sets ok to false on error
|
||||
function check(subref, subother, path)
|
||||
{
|
||||
//console.log("checking");
|
||||
//console.log("path: " + path);
|
||||
//console.log("subref: " +subref);
|
||||
//console.log("subother: "+subother);
|
||||
if(subref instanceof Array)
|
||||
{
|
||||
//console.log("is Array: " + path);
|
||||
if(!(subother instanceof Array))
|
||||
{
|
||||
ok = false;
|
||||
console.log("Error: not an Array " + path);
|
||||
return;
|
||||
}
|
||||
if(subother.length == 0)
|
||||
{
|
||||
console.log("Warning: can't check Array of lentgh 0 " + path);
|
||||
return;
|
||||
}
|
||||
// check first array member
|
||||
check(subref[0], subother[0], path);
|
||||
return;
|
||||
}
|
||||
// else compare as dict
|
||||
for(m in subref)
|
||||
{
|
||||
if(!(m in subother))
|
||||
{
|
||||
ok = false;
|
||||
console.log("Error: missing member \"" + m + "\" in "+ path);
|
||||
continue;
|
||||
}
|
||||
if(subref[m] === undefined)
|
||||
{
|
||||
// undefined = don't care what it is
|
||||
continue;
|
||||
}
|
||||
if(typeof(subref[m]) == typeof(subother[m]))
|
||||
{
|
||||
if(typeof(subref[m]) == "object")
|
||||
{
|
||||
// make deep object inspection
|
||||
path.push(m);
|
||||
check(subref[m], subother[m], path);
|
||||
path.pop();
|
||||
}
|
||||
// else everthing is fine
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
console.log("Error: member \"" + m + "\" has wrong type in "+ path);
|
||||
}
|
||||
}
|
||||
// TODO: check for additional members and print notice
|
||||
}
|
||||
|
||||
check(ref, other, []);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
function stringifyTypes(obj)
|
||||
{
|
||||
if(obj instanceof Array)
|
||||
{
|
||||
return [stringifyTypes(obj[0])];
|
||||
}
|
||||
var ret = {};
|
||||
for(m in obj)
|
||||
{
|
||||
if(typeof(obj[m]) === "object")
|
||||
ret[m] = stringifyTypes(obj[m]);
|
||||
else
|
||||
ret[m] = typeof obj[m];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// trick to get multiline string constants: use comment as string constant
|
||||
var input = function(){/*
|
||||
[{
|
||||
"locations": [{
|
||||
"avatar_address": "/5cfed435ebc24d2d0842f50c6443ec76/avatar_image",
|
||||
"groups": null,
|
||||
"is_online": true,
|
||||
"location": "",
|
||||
"name": "se2",
|
||||
"peer_id": "5cfed435ebc24d2d0842f50c6443ec76",
|
||||
"pgp_id": "985CAD914B19A212"
|
||||
}],
|
||||
"name": "se2"
|
||||
}]
|
||||
*/}.toString().slice(14,-3);
|
||||
|
||||
// **************** end of old stuff ***************************
|
@ -3,7 +3,8 @@ body {
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
margin: 0em;
|
||||
padding: 1.5em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
@ -36,12 +37,40 @@ td{
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.btn2{
|
||||
border-style: solid;
|
||||
border-color: lime;
|
||||
border-radius: 3mm;
|
||||
padding: 2mm;
|
||||
font-size: 10mm;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
.btn2:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
background-color: black;
|
||||
border-color: lime;
|
||||
font-size: 10mm;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 2mm;
|
||||
margin-right: 2mm;
|
||||
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
#logo_splash{
|
||||
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
|
@ -69,7 +69,7 @@ var signalSlotServer =
|
||||
/**
|
||||
* Unregister a previously registered client.
|
||||
*/
|
||||
unregisterClient : function(client) // no token as parameter, assuming unregister from all listening tokens
|
||||
unregisterClient : function(client)
|
||||
{
|
||||
var to_delete = [];
|
||||
var clients = this.clients;
|
||||
@ -139,6 +139,43 @@ var SignalSlotMixin =
|
||||
},
|
||||
};
|
||||
|
||||
// url hash handling
|
||||
// - allows the backwards/forward button to work
|
||||
// - restores same view for same url
|
||||
// dataflow:
|
||||
// emit change_url event -> event handler sets browser url hash
|
||||
// -> browser sends hash changed event -> emit url_changed event
|
||||
var prev_url = "";
|
||||
function hashChanged(){
|
||||
var url = window.location.hash.slice(1); // remove #
|
||||
// prevent double work
|
||||
//if(url !== prev_url)
|
||||
if(true)
|
||||
{
|
||||
signalSlotServer.emitSignal("url_changed", {url: url});
|
||||
prev_url = url;
|
||||
}
|
||||
}
|
||||
// Listen on hash change:
|
||||
window.addEventListener('hashchange', hashChanged);
|
||||
// Listen on page load:
|
||||
// this does not work, because the components are not always mounted when the event fires
|
||||
window.addEventListener('load', hashChanged);
|
||||
|
||||
var changeUrlListener = {
|
||||
onSignal: function(signal_name, parameters){
|
||||
if(signal_name === "change_url")
|
||||
{
|
||||
console.log("changeUrlListener: change url to "+parameters.url);
|
||||
window.location.hash = parameters.url;
|
||||
// some browsers dont send an event, so trigger event by hand
|
||||
// the history does not work then
|
||||
hashChanged();
|
||||
}
|
||||
},
|
||||
};
|
||||
signalSlotServer.registerClient(changeUrlListener);
|
||||
|
||||
var Peers = React.createClass({
|
||||
mixins: [AutoUpdateMixin],
|
||||
getPath: function(){return "peers";},
|
||||
@ -161,7 +198,7 @@ var Peers2 = React.createClass({
|
||||
return {data: []};
|
||||
},
|
||||
add_friend_handler: function(){
|
||||
this.emit("url_changed", {url: "add_friend"});
|
||||
this.emit("change_url", {url: "add_friend"});
|
||||
},
|
||||
render: function(){
|
||||
var component = this;
|
||||
@ -202,7 +239,7 @@ var Peers2 = React.createClass({
|
||||
return (
|
||||
<div>
|
||||
{/* span reduces width to only the text length, div does not */}
|
||||
<span onClick={this.add_friend_handler} className="btn">+ add friend</span>
|
||||
<div onClick={this.add_friend_handler} className="btn2">+ add friend</div>
|
||||
<table>
|
||||
<tr><th>avatar</th><th> name </th><th> locations</th><th></th></tr>
|
||||
{this.state.data.map(function(peer){ return <Peer key={peer.name} data={peer}/>; })}
|
||||
@ -465,14 +502,15 @@ var PasswordWidget = React.createClass({
|
||||
render: function(){
|
||||
if(this.state.data.want_password === false)
|
||||
{
|
||||
return(<p>PasswordWidget: nothing to do.</p>);
|
||||
return <div></div>;
|
||||
//return(<p>PasswordWidget: nothing to do.</p>);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(
|
||||
<div>
|
||||
<p>Enter password for key {this.state.data.key_name}</p>
|
||||
<input type="text" ref="password" />
|
||||
<input type="password" ref="password" />
|
||||
<input
|
||||
type="button"
|
||||
value="ok"
|
||||
@ -487,7 +525,7 @@ var PasswordWidget = React.createClass({
|
||||
var AccountSelectWidget = React.createClass({
|
||||
mixins: [AutoUpdateMixin],
|
||||
getInitialState: function(){
|
||||
return {data: []};
|
||||
return {mode: "list", data: []};
|
||||
},
|
||||
getPath: function(){
|
||||
return "control/locations";
|
||||
@ -495,14 +533,20 @@ var AccountSelectWidget = React.createClass({
|
||||
selectAccount: function(id){
|
||||
console.log("login with id="+id)
|
||||
RS.request({path: "control/login", data:{id: id}});
|
||||
var state = this.state;
|
||||
state.mode = "wait";
|
||||
this.setState(state);
|
||||
},
|
||||
render: function(){
|
||||
var component = this;
|
||||
if(this.state.mode === "wait")
|
||||
return <p>waiting...</p>;
|
||||
else
|
||||
return(
|
||||
<div>
|
||||
<div><p>select a location to log in</p></div>
|
||||
{this.state.data.map(function(location){
|
||||
return <div key={location.id} className="btn" onClick ={function(){component.selectAccount(location.id);}}>{location.name} ({location.location})</div>;
|
||||
return <div className="btn2" key={location.id} onClick ={function(){component.selectAccount(location.id);}}>{location.name} ({location.location})</div>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
@ -541,10 +585,50 @@ var LoginWidget = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
var MainWidget = React.createClass({
|
||||
var Menu = React.createClass({
|
||||
mixins: [SignalSlotMixin],
|
||||
getInitialState: function(){
|
||||
return {page: "login"};
|
||||
return {};
|
||||
},
|
||||
componentWillMount: function()
|
||||
{
|
||||
},
|
||||
render: function(){
|
||||
var outer = this;
|
||||
return (
|
||||
<div>
|
||||
<div className="btn2" onClick={function(){outer.emit("change_url", {url: "main"});}}>
|
||||
Start
|
||||
</div>
|
||||
<div className="btn2" onClick={function(){outer.emit("change_url", {url: "login"});}}>
|
||||
Login
|
||||
</div>
|
||||
<div className="btn2" onClick={function(){outer.emit("change_url", {url: "friends"});}}>
|
||||
Friends
|
||||
</div>
|
||||
<div className="btn2" onClick={function(){outer.emit("change_url", {url: "downloads"});}}>
|
||||
Downloads
|
||||
</div>
|
||||
<div className="btn2" onClick={function(){outer.emit("change_url", {url: "search"});}}>
|
||||
Search
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var MainWidget = React.createClass({
|
||||
mixins: [SignalSlotMixin, AutoUpdateMixin],
|
||||
getPath: function(){
|
||||
return "control/runstate";
|
||||
},
|
||||
getInitialState: function(){
|
||||
// hack: get hash
|
||||
var url = window.location.hash.slice(1);
|
||||
if(url === "")
|
||||
url = "menu";
|
||||
return {page: url, data: {runstate: "waiting_for_server"}};
|
||||
//return {page: "login"};
|
||||
},
|
||||
componentWillMount: function()
|
||||
{
|
||||
@ -553,20 +637,38 @@ var MainWidget = React.createClass({
|
||||
function(params)
|
||||
{
|
||||
console.log("MainWidget received url_changed. url="+params.url);
|
||||
outer.setState({page: params.url});
|
||||
var url = params.url;
|
||||
if(url.length === 0)
|
||||
url = "menu";
|
||||
outer.setState({page: url});
|
||||
});
|
||||
},
|
||||
render: function(){
|
||||
var outer = this;
|
||||
var mainpage = <p>page not implemented: {this.state.page}</p>;
|
||||
|
||||
if(this.state.data.runstate === "waiting_for_server")
|
||||
{
|
||||
mainpage = <div><p>waiting for reply from server...<br/>please wait...</p></div>;
|
||||
}
|
||||
if(this.state.data.runstate === "waiting_init" || this.state.data.runstate === "waiting_account_select")
|
||||
{
|
||||
mainpage = <LoginWidget/>;
|
||||
}
|
||||
if(this.state.data.runstate === "running_ok")
|
||||
{
|
||||
if(this.state.page === "main")
|
||||
{
|
||||
mainpage = <p>
|
||||
mainpage = <div><p>
|
||||
A new webinterface for Retroshare. Build with react.js.
|
||||
React allows to build a modern and user friendly single page application.
|
||||
The component system makes this very simple.
|
||||
Updating the GUI is also very simple: one React mixin can handle updating for all components.
|
||||
</p>;
|
||||
</p>
|
||||
|
||||
<div className="btn2">Div Button</div>
|
||||
<div className="btn2">Div Button</div>
|
||||
</div>;
|
||||
}
|
||||
if(this.state.page === "friends")
|
||||
{
|
||||
@ -592,27 +694,39 @@ var MainWidget = React.createClass({
|
||||
{
|
||||
mainpage = <LoginWidget/>;
|
||||
}
|
||||
if(this.state.page === "menu")
|
||||
{
|
||||
mainpage = <Menu/>;
|
||||
}
|
||||
}
|
||||
|
||||
var menubutton = <div onClick={function(){outer.emit("change_url", {url: "menu"});}} className="btn2"><- menu</div>;
|
||||
if(this.state.page === "menu")
|
||||
menubutton = <div>Retroshare webinterface</div>;
|
||||
return (
|
||||
<div>
|
||||
<PasswordWidget/>
|
||||
<AudioPlayerWidget/>
|
||||
{/*
|
||||
<ul className="nav">
|
||||
<li onClick={function(){outer.emit("url_changed", {url: "main"});}}>
|
||||
<li onClick={function(){outer.emit("change_url", {url: "main"});}}>
|
||||
Start
|
||||
</li>
|
||||
<li onClick={function(){outer.emit("url_changed", {url: "login"});}}>
|
||||
<li onClick={function(){outer.emit("change_url", {url: "login"});}}>
|
||||
Login
|
||||
</li>
|
||||
<li onClick={function(){outer.emit("url_changed", {url: "friends"});}}>
|
||||
<li onClick={function(){outer.emit("change_url", {url: "friends"});}}>
|
||||
Friends
|
||||
</li>
|
||||
<li onClick={function(){outer.emit("url_changed", {url: "downloads"});}}>
|
||||
<li onClick={function(){outer.emit("change_url", {url: "downloads"});}}>
|
||||
Downloads
|
||||
</li>
|
||||
<li onClick={function(){outer.emit("url_changed", {url: "search"});}}>
|
||||
<li onClick={function(){outer.emit("change_url", {url: "search"});}}>
|
||||
Search
|
||||
</li>
|
||||
</ul>
|
||||
*/}
|
||||
{menubutton}
|
||||
{mainpage}
|
||||
</div>
|
||||
);
|
||||
|
@ -14,11 +14,13 @@
|
||||
<!-- automatic page reload -->
|
||||
<!--<script src="http://localhost:9091"></script>-->
|
||||
<!-- load this last, because it contains errors -->
|
||||
<script src="http://localhost:35729/livereload.js"></script>
|
||||
<!--<script src="http://localhost:35729/livereload.js"></script>-->
|
||||
<script src="http://192.168.1.102:35720/livereload.js"></script>
|
||||
|
||||
<link href="green-black.css" rel="stylesheet">
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<p>loading lots of stuff...</p>
|
||||
|
Loading…
Reference in New Issue
Block a user