mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-29 01:16:20 -05:00
webui:
- redesigned friends list - added friendly units on downloads page - added progress bar on downloads page - fixed makefile directories and updated readme git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@8202 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
parent
b999ccc34b
commit
7c1cab07e8
@ -2,4 +2,13 @@ libresapi: resource_api and new webinterface
|
||||
============================================
|
||||
|
||||
* ./api contains a C++ backend to control retroshare from webinterfaces or scripting
|
||||
* ./webui contains HTML/CSS/JavaScript files for the webinterface
|
||||
* ./webfiles contains compiled files for the webinterface
|
||||
* ./webui contains HTML/CSS/JavaScript source files for the webinterface
|
||||
|
||||
Quickinfo for builders and packagers
|
||||
====================================
|
||||
|
||||
* copy the files in ./webfiles to
|
||||
* ./webui (Windows)
|
||||
* /usr/share/RetroShare0.6/webui (Linux)
|
||||
* other OS: see RsAccountsDetail::PathDataDirectory()
|
@ -71,6 +71,23 @@ input:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.flexbox{
|
||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
|
||||
display: -ms-flexbox; /* TWEENER - IE 10 */
|
||||
display: -webkit-flex; /* NEW - Chrome */
|
||||
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
.flexwidemember{
|
||||
-webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
-moz-box-flex: 1; /* OLD - Firefox 19- */
|
||||
width: 20%; /* For old syntax, otherwise collapses. */
|
||||
-webkit-flex: 1; /* Chrome */
|
||||
-ms-flex: 1; /* IE 10 */
|
||||
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
#logo_splash{
|
||||
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
|
@ -188,21 +188,6 @@ var changeUrlListener = {
|
||||
};
|
||||
signalSlotServer.registerClient(changeUrlListener);
|
||||
|
||||
var Peers = React.createClass({
|
||||
mixins: [AutoUpdateMixin],
|
||||
getPath: function(){return "peers";},
|
||||
getInitialState: function(){
|
||||
return {data: []};
|
||||
},
|
||||
render: function(){
|
||||
var renderOne = function(f){
|
||||
console.log("make one");
|
||||
return <p>{f.name} <img src={api_url+f.locations[0].avatar_address} /></p>;
|
||||
};
|
||||
return <div>{this.state.data.map(renderOne)}</div>;
|
||||
},
|
||||
});
|
||||
|
||||
var Peers2 = React.createClass({
|
||||
mixins: [AutoUpdateMixin, SignalSlotMixin],
|
||||
getPath: function(){return "peers";},
|
||||
@ -260,6 +245,81 @@ var Peers2 = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
var Peers3 = React.createClass({
|
||||
mixins: [AutoUpdateMixin, SignalSlotMixin],
|
||||
getPath: function(){return "peers";},
|
||||
getInitialState: function(){
|
||||
return {data: []};
|
||||
},
|
||||
add_friend_handler: function(){
|
||||
this.emit("change_url", {url: "add_friend"});
|
||||
},
|
||||
render: function(){
|
||||
var component = this;
|
||||
var Peer = React.createClass({
|
||||
remove_peer_handler: function(){
|
||||
var yes = window.confirm("Remove "+this.props.data.name+" from friendslist?");
|
||||
if(yes){
|
||||
RS.request({path: component.getPath()+"/"+this.props.data.pgp_id+"/delete"});
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var locations = this.props.data.locations.map(function(loc){
|
||||
var online_style = {
|
||||
width: "1em",
|
||||
height: "1em",
|
||||
borderRadius: "0.5em",
|
||||
backgroundColor: "grey",
|
||||
float:"left",
|
||||
};
|
||||
if(loc.is_online)
|
||||
online_style.backgroundColor = "lime";
|
||||
return(<div key={loc.peer_id} style={{color: loc.is_online? "lime": "grey"}}>{/*<div style={online_style}></div>*/}{loc.location}</div>);
|
||||
});
|
||||
var avatars = this.props.data.locations.map(function(loc){
|
||||
if(loc.is_online)
|
||||
{
|
||||
var avatar_url = api_url + component.getPath() + loc.avatar_address;
|
||||
return <img style={{borderRadius: "3mm", margin: "2mm"}} src={avatar_url}/>;
|
||||
}
|
||||
else return <span></span>;
|
||||
});
|
||||
var remove_button_style = {
|
||||
color: "red",
|
||||
//fontSize: "1.5em",
|
||||
fontSize: "10mm",
|
||||
padding: "0.2em",
|
||||
cursor: "pointer",
|
||||
};
|
||||
var remove_button = <div onClick={this.remove_peer_handler} style={remove_button_style}>X</div>;
|
||||
var is_online = false;
|
||||
for(var i in this.props.data.locations)
|
||||
{
|
||||
if(this.props.data.locations[i].is_online)
|
||||
is_online = true;
|
||||
}
|
||||
return(
|
||||
<div className="flexbox" style={{color: is_online? "lime": "grey"}}>
|
||||
<div>{avatars}</div>
|
||||
<div className="flexwidemember">
|
||||
<h1 style={{marginBottom: "1mm"}}>{this.props.data.name}</h1>
|
||||
<div>{locations}</div>
|
||||
</div>
|
||||
{remove_button}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
{/* span reduces width to only the text length, div does not */}
|
||||
<div onClick={this.add_friend_handler} className="btn2">+ add friend</div>
|
||||
{this.state.data.map(function(peer){ return <Peer key={peer.pgp_id} data={peer}/>; })}
|
||||
</div>);
|
||||
},
|
||||
});
|
||||
|
||||
var AddPeerWidget = React.createClass({
|
||||
getInitialState: function(){
|
||||
return {page: "start"};
|
||||
@ -309,6 +369,35 @@ var AddPeerWidget = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
var ProgressBar = React.createClass({
|
||||
render: function(){
|
||||
return(
|
||||
<div style={{
|
||||
borderStyle: "solid",
|
||||
borderColor: "lime",
|
||||
borderRadius: "3mm",
|
||||
padding: "2mm",
|
||||
height: "10mm",
|
||||
}}>
|
||||
<div style={{backgroundColor: "lime", height: "100%", width: (this.props.progress*100)+"%",}}></div>
|
||||
</div>);
|
||||
}
|
||||
});
|
||||
|
||||
function makeFriendlyUnit(bytes)
|
||||
{
|
||||
if(bytes < 1e3)
|
||||
return bytes.toFixed(1) + "B";
|
||||
if(bytes < 1e3)
|
||||
return (bytes/1e3).toFixed(1) + "kB";
|
||||
if(bytes < 1e9)
|
||||
return (bytes/1e6).toFixed(1) + "MB";
|
||||
if(bytes < 1e12)
|
||||
return (bytes/1e9).toFixed(1) + "GB";
|
||||
return (bytes/1e12).toFixed(1) + "TB";
|
||||
}
|
||||
|
||||
|
||||
var DownloadsWidget = React.createClass({
|
||||
mixins: [AutoUpdateMixin, SignalSlotMixin],
|
||||
getPath: function(){ return "transfers/downloads";},
|
||||
@ -372,8 +461,8 @@ var DownloadsWidget = React.createClass({
|
||||
}
|
||||
return(<tr>
|
||||
<td>{this.props.data.name}</td>
|
||||
<td>{this.props.data.size}</td>
|
||||
<td>{this.props.data.transfered / this.props.data.size}</td>
|
||||
<td>{makeFriendlyUnit(this.props.data.size)}</td>
|
||||
<td><ProgressBar progress={this.props.data.transfered / this.props.data.size}/></td>
|
||||
<td>{this.props.data.download_status}</td>
|
||||
<td>{ctrlBtn} <div className="btn" onClick={cancelFn}>cancel</div> {playBtn}</td>
|
||||
</tr>);
|
||||
@ -728,7 +817,7 @@ var MainWidget = React.createClass({
|
||||
}
|
||||
if(this.state.page === "friends")
|
||||
{
|
||||
mainpage = <Peers2 />;
|
||||
mainpage = <Peers3 />;
|
||||
}
|
||||
if(this.state.page === "downloads")
|
||||
{
|
||||
@ -765,6 +854,7 @@ var MainWidget = React.createClass({
|
||||
<AudioPlayerWidget/>
|
||||
{menubutton}
|
||||
{mainpage}
|
||||
{/*<ProgressBar progress={0.7}/>*/}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -1,35 +1,36 @@
|
||||
REACT_VERSION = 0.13.1
|
||||
|
||||
JSEXTLIBS = dist/react.js dist/JSXTransformer.js
|
||||
DISTDIR = ../webfiles
|
||||
JSEXTLIBS = $(DISTDIR)/react.js $(DISTDIR)/JSXTransformer.js
|
||||
JSLIBS = RsXHRConnection.js RsApi.js
|
||||
HTML = index.html
|
||||
JSGUI = gui.jsx
|
||||
CSS = green-black.css
|
||||
|
||||
all: dist $(JSEXTLIBS) $(addprefix dist/, $(JSLIBS)) $(addprefix dist/, $(HTML)) $(addprefix dist/, $(JSGUI)) $(addprefix dist/, $(CSS))
|
||||
all: $(DISTDIR) $(JSEXTLIBS) $(addprefix $(DISTDIR)/, $(JSLIBS)) $(addprefix $(DISTDIR)/, $(HTML)) $(addprefix $(DISTDIR)/, $(JSGUI)) $(addprefix $(DISTDIR)/, $(CSS))
|
||||
.PHONY: all
|
||||
|
||||
dist/livereload: dist $(JSEXTLIBS) $(addprefix dist/, $(JSLIBS)) $(addprefix dist/, $(HTML)) $(addprefix dist/, $(JSGUI)) $(addprefix dist/, $(CSS))
|
||||
$(DISTDIR)/livereload: $(DISTDIR) $(JSEXTLIBS) $(addprefix $(DISTDIR)/, $(JSLIBS)) $(addprefix $(DISTDIR)/, $(HTML)) $(addprefix $(DISTDIR)/, $(JSGUI)) $(addprefix $(DISTDIR)/, $(CSS))
|
||||
wget -qO- http://localhost:9090/api/v2/livereload/trigger
|
||||
touch dist/livereload
|
||||
touch $(DISTDIR)/livereload
|
||||
|
||||
dist/react.js:
|
||||
cd dist && wget --no-check-certificate --output-document react.js http://fb.me/react-$(REACT_VERSION).js
|
||||
$(DISTDIR)/react.js:
|
||||
cd $(DISTDIR) && wget --no-check-certificate --output-document react.js http://fb.me/react-$(REACT_VERSION).js
|
||||
|
||||
dist/JSXTransformer.js:
|
||||
cd dist && wget --no-check-certificate --output-document JSXTransformer.js http://fb.me/JSXTransformer-$(REACT_VERSION).js
|
||||
$(DISTDIR)/JSXTransformer.js:
|
||||
cd $(DISTDIR) && wget --no-check-certificate --output-document JSXTransformer.js http://fb.me/JSXTransformer-$(REACT_VERSION).js
|
||||
|
||||
$(addprefix dist/, $(JSLIBS)): dist/%: %
|
||||
$(addprefix $(DISTDIR)/, $(JSLIBS)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
$(addprefix dist/, $(HTML)): dist/%: %
|
||||
$(addprefix $(DISTDIR)/, $(HTML)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
$(addprefix dist/, $(JSGUI)): dist/%: %
|
||||
$(addprefix $(DISTDIR)/, $(JSGUI)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
$(addprefix dist/, $(CSS)): dist/%: %
|
||||
$(addprefix $(DISTDIR)/, $(CSS)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
dist:
|
||||
mkdir dist
|
||||
$(DISTDIR):
|
||||
mkdir $(DISTDIR)
|
||||
|
@ -12,9 +12,11 @@ BUILD / INSTALLATION
|
||||
|
||||
- run (requires wget, use MinGW shell on Windows)
|
||||
make
|
||||
- all output files are now in the "dist" folder
|
||||
- all output files are now in libresapi/src/webfiles
|
||||
- use the --webinterface 9090 command line parameter to enable webui in retroshare-nogui
|
||||
- set the --docroot parameter of retroshare-nogui to point to the "dist" directory
|
||||
- set the --docroot parameter of retroshare-nogui to point to the "libresapi/src/webfiles" directory
|
||||
(or symlink from /usr/share/RetroShare0.6/webui on Linux, ./webui on Windows)
|
||||
- retroshare-gui does not have a --docroot parameter. Use symlinks then.
|
||||
|
||||
DEVELOPMENT
|
||||
-----------
|
||||
@ -26,8 +28,8 @@ DEVELOPMENT
|
||||
npm install
|
||||
- run Retroshare with webinterface on port 9090
|
||||
- during development, run this command (use MinGW shell on Windows)
|
||||
while true; do make dist/livereload --silent; sleep 1; done
|
||||
- the command will copy the source files to the "dist" directory if they change
|
||||
while true; do make ../webfiles/livereload --silent; sleep 1; done
|
||||
- the command will copy the source files to libresapi/src/webfiles if they change
|
||||
- it will trigger livereload at http://localhost:9090/api/v2/livereload/trigger
|
||||
|
||||
API DOCUMENTATION
|
||||
|
@ -71,6 +71,23 @@ input:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.flexbox{
|
||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
|
||||
display: -ms-flexbox; /* TWEENER - IE 10 */
|
||||
display: -webkit-flex; /* NEW - Chrome */
|
||||
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
.flexwidemember{
|
||||
-webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
-moz-box-flex: 1; /* OLD - Firefox 19- */
|
||||
width: 20%; /* For old syntax, otherwise collapses. */
|
||||
-webkit-flex: 1; /* Chrome */
|
||||
-ms-flex: 1; /* IE 10 */
|
||||
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
#logo_splash{
|
||||
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
|
@ -188,21 +188,6 @@ var changeUrlListener = {
|
||||
};
|
||||
signalSlotServer.registerClient(changeUrlListener);
|
||||
|
||||
var Peers = React.createClass({
|
||||
mixins: [AutoUpdateMixin],
|
||||
getPath: function(){return "peers";},
|
||||
getInitialState: function(){
|
||||
return {data: []};
|
||||
},
|
||||
render: function(){
|
||||
var renderOne = function(f){
|
||||
console.log("make one");
|
||||
return <p>{f.name} <img src={api_url+f.locations[0].avatar_address} /></p>;
|
||||
};
|
||||
return <div>{this.state.data.map(renderOne)}</div>;
|
||||
},
|
||||
});
|
||||
|
||||
var Peers2 = React.createClass({
|
||||
mixins: [AutoUpdateMixin, SignalSlotMixin],
|
||||
getPath: function(){return "peers";},
|
||||
@ -260,6 +245,81 @@ var Peers2 = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
var Peers3 = React.createClass({
|
||||
mixins: [AutoUpdateMixin, SignalSlotMixin],
|
||||
getPath: function(){return "peers";},
|
||||
getInitialState: function(){
|
||||
return {data: []};
|
||||
},
|
||||
add_friend_handler: function(){
|
||||
this.emit("change_url", {url: "add_friend"});
|
||||
},
|
||||
render: function(){
|
||||
var component = this;
|
||||
var Peer = React.createClass({
|
||||
remove_peer_handler: function(){
|
||||
var yes = window.confirm("Remove "+this.props.data.name+" from friendslist?");
|
||||
if(yes){
|
||||
RS.request({path: component.getPath()+"/"+this.props.data.pgp_id+"/delete"});
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var locations = this.props.data.locations.map(function(loc){
|
||||
var online_style = {
|
||||
width: "1em",
|
||||
height: "1em",
|
||||
borderRadius: "0.5em",
|
||||
backgroundColor: "grey",
|
||||
float:"left",
|
||||
};
|
||||
if(loc.is_online)
|
||||
online_style.backgroundColor = "lime";
|
||||
return(<div key={loc.peer_id} style={{color: loc.is_online? "lime": "grey"}}>{/*<div style={online_style}></div>*/}{loc.location}</div>);
|
||||
});
|
||||
var avatars = this.props.data.locations.map(function(loc){
|
||||
if(loc.is_online)
|
||||
{
|
||||
var avatar_url = api_url + component.getPath() + loc.avatar_address;
|
||||
return <img style={{borderRadius: "3mm", margin: "2mm"}} src={avatar_url}/>;
|
||||
}
|
||||
else return <span></span>;
|
||||
});
|
||||
var remove_button_style = {
|
||||
color: "red",
|
||||
//fontSize: "1.5em",
|
||||
fontSize: "10mm",
|
||||
padding: "0.2em",
|
||||
cursor: "pointer",
|
||||
};
|
||||
var remove_button = <div onClick={this.remove_peer_handler} style={remove_button_style}>X</div>;
|
||||
var is_online = false;
|
||||
for(var i in this.props.data.locations)
|
||||
{
|
||||
if(this.props.data.locations[i].is_online)
|
||||
is_online = true;
|
||||
}
|
||||
return(
|
||||
<div className="flexbox" style={{color: is_online? "lime": "grey"}}>
|
||||
<div>{avatars}</div>
|
||||
<div className="flexwidemember">
|
||||
<h1 style={{marginBottom: "1mm"}}>{this.props.data.name}</h1>
|
||||
<div>{locations}</div>
|
||||
</div>
|
||||
{remove_button}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
{/* span reduces width to only the text length, div does not */}
|
||||
<div onClick={this.add_friend_handler} className="btn2">+ add friend</div>
|
||||
{this.state.data.map(function(peer){ return <Peer key={peer.pgp_id} data={peer}/>; })}
|
||||
</div>);
|
||||
},
|
||||
});
|
||||
|
||||
var AddPeerWidget = React.createClass({
|
||||
getInitialState: function(){
|
||||
return {page: "start"};
|
||||
@ -309,6 +369,35 @@ var AddPeerWidget = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
var ProgressBar = React.createClass({
|
||||
render: function(){
|
||||
return(
|
||||
<div style={{
|
||||
borderStyle: "solid",
|
||||
borderColor: "lime",
|
||||
borderRadius: "3mm",
|
||||
padding: "2mm",
|
||||
height: "10mm",
|
||||
}}>
|
||||
<div style={{backgroundColor: "lime", height: "100%", width: (this.props.progress*100)+"%",}}></div>
|
||||
</div>);
|
||||
}
|
||||
});
|
||||
|
||||
function makeFriendlyUnit(bytes)
|
||||
{
|
||||
if(bytes < 1e3)
|
||||
return bytes.toFixed(1) + "B";
|
||||
if(bytes < 1e3)
|
||||
return (bytes/1e3).toFixed(1) + "kB";
|
||||
if(bytes < 1e9)
|
||||
return (bytes/1e6).toFixed(1) + "MB";
|
||||
if(bytes < 1e12)
|
||||
return (bytes/1e9).toFixed(1) + "GB";
|
||||
return (bytes/1e12).toFixed(1) + "TB";
|
||||
}
|
||||
|
||||
|
||||
var DownloadsWidget = React.createClass({
|
||||
mixins: [AutoUpdateMixin, SignalSlotMixin],
|
||||
getPath: function(){ return "transfers/downloads";},
|
||||
@ -372,8 +461,8 @@ var DownloadsWidget = React.createClass({
|
||||
}
|
||||
return(<tr>
|
||||
<td>{this.props.data.name}</td>
|
||||
<td>{this.props.data.size}</td>
|
||||
<td>{this.props.data.transfered / this.props.data.size}</td>
|
||||
<td>{makeFriendlyUnit(this.props.data.size)}</td>
|
||||
<td><ProgressBar progress={this.props.data.transfered / this.props.data.size}/></td>
|
||||
<td>{this.props.data.download_status}</td>
|
||||
<td>{ctrlBtn} <div className="btn" onClick={cancelFn}>cancel</div> {playBtn}</td>
|
||||
</tr>);
|
||||
@ -728,7 +817,7 @@ var MainWidget = React.createClass({
|
||||
}
|
||||
if(this.state.page === "friends")
|
||||
{
|
||||
mainpage = <Peers2 />;
|
||||
mainpage = <Peers3 />;
|
||||
}
|
||||
if(this.state.page === "downloads")
|
||||
{
|
||||
@ -765,6 +854,7 @@ var MainWidget = React.createClass({
|
||||
<AudioPlayerWidget/>
|
||||
{menubutton}
|
||||
{mainpage}
|
||||
{/*<ProgressBar progress={0.7}/>*/}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user