mirror of
https://github.com/RetroShare/RSNewWebUI.git
synced 2026-09-14 11:05:47 +05:00
try to fix eslint and prettier issues (#32)
* fix eslint + prettier issues * fix eslint config (remove with prettier clashing indent rules) * fix eslint warnings
This commit is contained in:
parent
6bbdb04824
commit
f09d6b0ca9
@ -17,10 +17,6 @@
|
||||
},
|
||||
"ignorePatterns": ["mithril.js", "assets/*", "make-src/*"],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
|
||||
@ -1,43 +1,124 @@
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
span,
|
||||
applet,
|
||||
object,
|
||||
iframe,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
a,
|
||||
abbr,
|
||||
acronym,
|
||||
address,
|
||||
big,
|
||||
cite,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
em,
|
||||
img,
|
||||
ins,
|
||||
kbd,
|
||||
q,
|
||||
s,
|
||||
samp,
|
||||
small,
|
||||
strike,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
tt,
|
||||
var,
|
||||
b,
|
||||
u,
|
||||
i,
|
||||
center,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
article,
|
||||
aside,
|
||||
canvas,
|
||||
details,
|
||||
embed,
|
||||
figure,
|
||||
figcaption,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
output,
|
||||
ruby,
|
||||
section,
|
||||
summary,
|
||||
time,
|
||||
mark,
|
||||
audio,
|
||||
video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
blockquote,
|
||||
q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
blockquote:before,
|
||||
blockquote:after,
|
||||
q:before,
|
||||
q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
@ -1,28 +1,20 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
const m = require('mithril');
|
||||
|
||||
|
||||
let Channel = () => {
|
||||
const Channel = () => {
|
||||
return {
|
||||
view: () => m('.channel'),
|
||||
};
|
||||
};
|
||||
|
||||
let MyChannels = () => {
|
||||
const MyChannels = () => {
|
||||
return {
|
||||
view: () => m('.widget', [
|
||||
m('h3', 'My channels'),
|
||||
m('hr'),
|
||||
m(Channel),
|
||||
])
|
||||
view: () => m('.widget', [m('h3', 'My channels'), m('hr'), m(Channel)]),
|
||||
};
|
||||
};
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: vnode => m('.tab-page', [
|
||||
m(MyChannels),
|
||||
]),
|
||||
view: (vnode) => m('.tab-page', [m(MyChannels)]),
|
||||
};
|
||||
};
|
||||
|
||||
@ -31,4 +23,3 @@ module.exports = {
|
||||
return m(Layout);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,160 +1,163 @@
|
||||
.lobby {
|
||||
margin: 10px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 20px;
|
||||
margin: 10px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.lobby .mainname {
|
||||
margin: 20px;
|
||||
font-weight: 100;
|
||||
font-size: 1.2em;
|
||||
margin: 20px;
|
||||
font-weight: 100;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.topic {
|
||||
color: #666;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.lobby > .topic {
|
||||
font-size: 0.95em;
|
||||
margin-left: 25px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.95em;
|
||||
margin-left: 25px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.lefttitle {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 100;
|
||||
font-size: 1.2em;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 100;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.leftname {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding:5px;
|
||||
font-weight: 100;
|
||||
font-size: 1em;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 5px;
|
||||
font-weight: 100;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.leftlobby > .topic {
|
||||
font-size: 0.75em;
|
||||
margin-left: 15px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.75em;
|
||||
margin-left: 15px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.subscribed, .public {
|
||||
cursor: pointer;
|
||||
.subscribed,
|
||||
.public {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.leftlobby {
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 10px;
|
||||
margin-top: 5px;
|
||||
background-color: white;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 10px;
|
||||
margin-top: 5px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.leftlobby.selected-lobby, .selectedidentity {
|
||||
color: white;
|
||||
background-color: #3ba4d7;
|
||||
.leftlobby.selected-lobby,
|
||||
.selectedidentity {
|
||||
color: white;
|
||||
background-color: #3ba4d7;
|
||||
}
|
||||
|
||||
.rightbar {
|
||||
position: absolute;
|
||||
width: 185px;
|
||||
background-color: white;
|
||||
overflow: auto;
|
||||
top: 130px;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
position: absolute;
|
||||
width: 185px;
|
||||
background-color: white;
|
||||
overflow: auto;
|
||||
top: 130px;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.user {
|
||||
padding: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.lobbyName {
|
||||
padding: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.lobbies {
|
||||
position: absolute;
|
||||
width: 185px;
|
||||
left:15px ;
|
||||
bottom: 15px;
|
||||
top: 130px;
|
||||
overflow:auto;
|
||||
position: absolute;
|
||||
width: 185px;
|
||||
left: 15px;
|
||||
bottom: 15px;
|
||||
top: 130px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.messages, .setup {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
top: 130px;
|
||||
left: 215px;
|
||||
right: 215px;
|
||||
overflow: auto;
|
||||
.messages,
|
||||
.setup {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
top: 130px;
|
||||
left: 215px;
|
||||
right: 215px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.messages {
|
||||
bottom: 115px;
|
||||
bottom: 115px;
|
||||
}
|
||||
|
||||
.messagetext {
|
||||
white-space: break-spaces;
|
||||
margin-right: 5px;
|
||||
white-space: break-spaces;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.message > * {
|
||||
margin-left: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.username {
|
||||
color: darkgreen;
|
||||
font-weight: bolder;
|
||||
color: darkgreen;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.chatMessage {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
height: 85px;
|
||||
bottom: 15px;
|
||||
right: 215px;
|
||||
left: 215px;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
height: 85px;
|
||||
bottom: 15px;
|
||||
right: 215px;
|
||||
left: 215px;
|
||||
}
|
||||
|
||||
textarea.chatMsg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chatatchar {
|
||||
margin-left: 0.2em;
|
||||
margin-right: 0.2em;
|
||||
color: silver;
|
||||
margin-left: 0.2em;
|
||||
margin-right: 0.2em;
|
||||
color: silver;
|
||||
}
|
||||
|
||||
.setupicon {
|
||||
margin-left: 1em;
|
||||
cursor: pointer;
|
||||
margin-left: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.leaveicon {
|
||||
margin-left: 1em;
|
||||
cursor: pointer;
|
||||
color: #d40000;
|
||||
margin-left: 1em;
|
||||
cursor: pointer;
|
||||
color: #d40000;
|
||||
}
|
||||
|
||||
.selectidentity {
|
||||
margin: 15px;
|
||||
font-size: 1.2em;
|
||||
margin: 15px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.setup > .identity {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.setup {
|
||||
bottom: 15px;
|
||||
bottom: 15px;
|
||||
}
|
||||
|
||||
.createDistantChat {
|
||||
margin-top: 1em;
|
||||
}
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
@ -1,273 +1,313 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let people_util = require('people/people_util');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const peopleUtil = require('people/people_util');
|
||||
|
||||
// **************** utility functions ********************
|
||||
|
||||
function loadLobbyDetails(id, apply) {
|
||||
rs.rsJsonApiRequest('/rsMsgs/getChatLobbyInfo', {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/getChatLobbyInfo',
|
||||
{
|
||||
id,
|
||||
},
|
||||
detail => {
|
||||
if (detail.retval) {
|
||||
apply(detail.info);
|
||||
}
|
||||
(detail) => {
|
||||
if (detail.retval) {
|
||||
apply(detail.info);
|
||||
}
|
||||
},
|
||||
true, {},
|
||||
true,
|
||||
{},
|
||||
undefined,
|
||||
// Custom serializer NOTE:
|
||||
// Since id represents 64-bit int(see deserializer note below)
|
||||
// Instead of using JSON.stringify, this function directly
|
||||
// creates a json string manually.
|
||||
() => '{"id":' + id + '}')
|
||||
}
|
||||
() => '{"id":' + id + '}'
|
||||
);
|
||||
}
|
||||
|
||||
function sortLobbies(lobbies){
|
||||
if (lobbies !== undefined){
|
||||
let list= [...lobbies];
|
||||
list.sort((a,b) => a.lobby_name.localeCompare(b.lobby_name));
|
||||
return list;
|
||||
}
|
||||
// return lobbies; // fallback on reload page in browser, keep undefiend
|
||||
}
|
||||
function sortLobbies(lobbies) {
|
||||
if (lobbies !== undefined) {
|
||||
const list = [...lobbies];
|
||||
list.sort((a, b) => a.lobby_name.localeCompare(b.lobby_name));
|
||||
return list;
|
||||
}
|
||||
// return lobbies; // fallback on reload page in browser, keep undefiend
|
||||
}
|
||||
|
||||
// ***************************** models ***********************************
|
||||
// ***************************** models ***********************************
|
||||
|
||||
let ChatRoomsModel = {
|
||||
const ChatRoomsModel = {
|
||||
allRooms: [],
|
||||
knownSubscrIds:[], // to exclude subscribed from public rooms (subscribedRooms filled to late)
|
||||
knownSubscrIds: [], // to exclude subscribed from public rooms (subscribedRooms filled to late)
|
||||
subscribedRooms: {},
|
||||
loadPublicRooms() {
|
||||
// TODO: this doesn't preserve id of rooms,
|
||||
// use regex on response to extract ids.
|
||||
rs.rsJsonApiRequest('/rsMsgs/getListOfNearbyChatLobbies', {},
|
||||
data => ChatRoomsModel.allRooms = sortLobbies(data.public_lobbies),
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/getListOfNearbyChatLobbies',
|
||||
{},
|
||||
(data) => (ChatRoomsModel.allRooms = sortLobbies(data.public_lobbies))
|
||||
);
|
||||
},
|
||||
loadSubscribedRooms(after = null) {
|
||||
// ChatRoomsModel.subscribedRooms = {};
|
||||
rs.rsJsonApiRequest('/rsMsgs/getChatLobbyList', {},
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/getChatLobbyList',
|
||||
{},
|
||||
// JS uses double precision numbers of 64 bit. It is equivalent
|
||||
// to 53 bits of precision. All large precision ints will
|
||||
// get truncated to an approximation.
|
||||
// This API uses Cpp-style 64 bits for `id`.
|
||||
// So we use the string-value 'xstr64' instead
|
||||
data => {
|
||||
let ids = data.cl_list.map(lid => lid.xstr64);
|
||||
(data) => {
|
||||
const ids = data.cl_list.map((lid) => lid.xstr64);
|
||||
ChatRoomsModel.knownSubscrIds = ids;
|
||||
let rooms = {};
|
||||
ids.map(id => loadLobbyDetails(id, info => {
|
||||
rooms[id]= info;
|
||||
if (Object.keys(rooms).length===ids.length) {
|
||||
// apply rooms to subscribedRooms only after reading all room-details, so sorting all or nothing
|
||||
ChatRoomsModel.subscribedRooms = rooms;
|
||||
}
|
||||
}));
|
||||
const rooms = {};
|
||||
ids.map((id) =>
|
||||
loadLobbyDetails(id, (info) => {
|
||||
rooms[id] = info;
|
||||
if (Object.keys(rooms).length === ids.length) {
|
||||
// apply rooms to subscribedRooms only after reading all room-details, so sorting all or nothing
|
||||
ChatRoomsModel.subscribedRooms = rooms;
|
||||
}
|
||||
})
|
||||
);
|
||||
if (after != null) {
|
||||
after()
|
||||
after();
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
subscribed(info) {
|
||||
return this.knownSubscrIds.includes(info.lobby_id.xstr64);
|
||||
},
|
||||
};
|
||||
|
||||
const ChatLobbyModel = {
|
||||
currentLobby: {
|
||||
lobby_name: '...',
|
||||
},
|
||||
lobby_user: '...',
|
||||
isSubscribed: false,
|
||||
messages: [],
|
||||
users: [],
|
||||
setupAction: (lobby_id, nick) => {},
|
||||
setIdentity(lobby_id, nick) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/setIdentityForChatLobby',
|
||||
{},
|
||||
() => m.route.set('/chat/:lobby_id',{lobby_id: lobby_id}),
|
||||
true,
|
||||
{},
|
||||
JSON.parse,
|
||||
()=> '{"lobby_id":' + lobby_id + ',"nick":"' + nick + '"}'
|
||||
);
|
||||
},
|
||||
enterPublicLobby(lobby_id, nick) {
|
||||
console.info('joinVisibleChatLobby', nick, '@', lobby_id)
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/joinVisibleChatLobby',
|
||||
{},
|
||||
() => {
|
||||
loadLobbyDetails(lobby_id, info => {
|
||||
ChatRoomsModel.subscribedRooms[lobby_id]= info;
|
||||
ChatRoomsModel.loadSubscribedRooms(() => {
|
||||
m.route.set('/chat/:lobby', { lobby: info.lobby_id.xstr64 });
|
||||
})
|
||||
});
|
||||
},
|
||||
true,
|
||||
{},
|
||||
JSON.parse,
|
||||
()=> '{"lobby_id":' + lobby_id + ',"own_id":"' + nick + '"}'
|
||||
);
|
||||
},
|
||||
unsubscribeChatLobby(lobby_id, follow) {
|
||||
console.info('unsubscribe lobby', lobby_id)
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/unsubscribeChatLobby',
|
||||
{},
|
||||
() => ChatRoomsModel.loadSubscribedRooms(follow),
|
||||
true,
|
||||
{},
|
||||
JSON.parse,
|
||||
() => '{"lobby_id":' + lobby_id + '}'
|
||||
)
|
||||
},
|
||||
chatId(action) {
|
||||
return {type:3,lobby_id:{xstr64:m.route.param('lobby')}};
|
||||
},
|
||||
loadLobby (currentlobbyid) {
|
||||
loadLobbyDetails(currentlobbyid, detail => {
|
||||
this.setupAction= this.setIdentity;
|
||||
this.currentLobby = detail;
|
||||
this.isSubscribed = true;
|
||||
this.lobby_user = rs.userList.username(detail.gxs_id) || '???';
|
||||
let lobbyid = currentlobbyid;
|
||||
// apply existing messages to current lobby view
|
||||
rs.events[15].chatMessages(this.chatId(),rs.events[15], l => (this.messages = l.map(msg=> m(Message, msg))));
|
||||
// register for chatEvents for future messages
|
||||
rs.events[15].notify = chatMessage => {
|
||||
if (chatMessage.chat_id.type===3 && chatMessage.chat_id.lobby_id.xstr64 === lobbyid) {
|
||||
this.messages.push(m(Message,chatMessage));
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
// lookup for chat-user names (only snapshot, we don't get notified about changes of participants)
|
||||
var names = detail.gxs_ids.reduce((a,u) => a.concat(rs.userList.username(u.key)), []);
|
||||
names.sort((a,b) => a.localeCompare(b));
|
||||
this.users = [];
|
||||
names.forEach(name => this.users = this.users.concat([m('.user',name)]));
|
||||
return this.users;
|
||||
});
|
||||
},
|
||||
loadPublicLobby (currentlobbyid) {
|
||||
console.info('loadPublicLobby ChatRoomsModel:',ChatRoomsModel);
|
||||
this.setupAction= this.enterPublicLobby;
|
||||
this.isSubscribed = false;
|
||||
ChatRoomsModel.allRooms.forEach(it => {
|
||||
if (it.lobby_id.xstr64 === currentlobbyid) {
|
||||
this.currentLobby = it;
|
||||
this.lobby_user = '???';
|
||||
this.lobbyid = currentlobbyid;
|
||||
}
|
||||
})
|
||||
this.users = []
|
||||
},
|
||||
sendMessage(msg, onsuccess) {
|
||||
rs.rsJsonApiRequest('/rsmsgs/sendChat', {},
|
||||
() => {
|
||||
// adding own message to log
|
||||
rs.events[15].handler({
|
||||
mChatMessage:{
|
||||
chat_id:this.chatId(),
|
||||
msg:msg,
|
||||
sendTime:new Date().getTime()/1000,
|
||||
lobby_peer_gxs_id:this.currentLobby.gxs_id,
|
||||
}
|
||||
},rs.events[15]);
|
||||
onsuccess();
|
||||
},
|
||||
true, {}, undefined,
|
||||
() => '{"id":{"type": 3,"lobby_id":' + m.route.param('lobby') + '}, "msg":' + JSON.stringify(msg) + '}'
|
||||
);
|
||||
},
|
||||
selected(info, selName, defaultName) {
|
||||
let currid = (ChatLobbyModel.currentLobby.lobby_id || {xstr64: m.route.param('lobby')}).xstr64
|
||||
return ((info.lobby_id.xstr64 === currid) ? selName : '') + defaultName;
|
||||
},
|
||||
switchToEvent(info) {
|
||||
return () => {
|
||||
ChatLobbyModel.currentLobby = info;
|
||||
m.route.set('/chat/:lobby', { lobby: info.lobby_id.xstr64 });
|
||||
ChatLobbyModel.loadLobby(info.lobby_id.xstr64); // update
|
||||
};
|
||||
},
|
||||
setupEvent(info) {
|
||||
return () => {
|
||||
m.route.set('/chat/:lobby/setup', { lobby: info.lobby_id.xstr64 });
|
||||
ChatLobbyModel.loadPublicLobby(info.lobby_id.xstr64); // update
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ************************* views ****************************
|
||||
|
||||
/**
|
||||
* Message displays a single Chat-Message<br>
|
||||
* currently removes formatting and in consequence inline links
|
||||
* msg: Message to Display
|
||||
*/
|
||||
* Message displays a single Chat-Message<br>
|
||||
* currently removes formatting and in consequence inline links
|
||||
* msg: Message to Display
|
||||
*/
|
||||
const Message = () => {
|
||||
let msg = null; // message to display
|
||||
let text = ''; // extracted text to display
|
||||
let datetime = ''; // date time to display
|
||||
let username = ''; // username to display (later may be linked)
|
||||
return {
|
||||
oninit: vnode => {
|
||||
console.info('chat Message',vnode);
|
||||
oninit: (vnode) => {
|
||||
console.info('chat Message', vnode);
|
||||
msg = vnode.attrs;
|
||||
datetime = new Date(msg.sendTime * 1000).toLocaleTimeString();
|
||||
username = rs.userList.username(msg.lobby_peer_gxs_id);
|
||||
text = msg.msg.replaceAll('<br/>','\n').replace(new RegExp('<style[^<]*</style>|<[^>]*>','gm'),'');
|
||||
text = msg.msg
|
||||
.replaceAll('<br/>', '\n')
|
||||
.replace(new RegExp('<style[^<]*</style>|<[^>]*>', 'gm'), '');
|
||||
console.info('chat Text', text);
|
||||
},
|
||||
view: () => m('.message', m('span.datetime', datetime), m('span.username', username), m('span.messagetext', text))
|
||||
view: () =>
|
||||
m(
|
||||
'.message',
|
||||
m('span.datetime', datetime),
|
||||
m('span.username', username),
|
||||
m('span.messagetext', text)
|
||||
),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const ChatLobbyModel = {
|
||||
currentLobby: {
|
||||
lobby_name: '...',
|
||||
},
|
||||
lobby_user: '...',
|
||||
isSubscribed: false,
|
||||
messages: [],
|
||||
users: [],
|
||||
setupAction: (lobbyId, nick) => {},
|
||||
setIdentity(lobbyId, nick) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/setIdentityForChatLobby',
|
||||
{},
|
||||
() => m.route.set('/chat/:lobby_id', { lobbyId }),
|
||||
true,
|
||||
{},
|
||||
JSON.parse,
|
||||
() => '{"lobby_id":' + lobbyId + ',"nick":"' + nick + '"}'
|
||||
);
|
||||
},
|
||||
enterPublicLobby(lobbyId, nick) {
|
||||
console.info('joinVisibleChatLobby', nick, '@', lobbyId);
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/joinVisibleChatLobby',
|
||||
{},
|
||||
() => {
|
||||
loadLobbyDetails(lobbyId, (info) => {
|
||||
ChatRoomsModel.subscribedRooms[lobbyId] = info;
|
||||
ChatRoomsModel.loadSubscribedRooms(() => {
|
||||
m.route.set('/chat/:lobby', { lobby: info.lobby_id.xstr64 });
|
||||
});
|
||||
});
|
||||
},
|
||||
true,
|
||||
{},
|
||||
JSON.parse,
|
||||
() => '{"lobby_id":' + lobbyId + ',"own_id":"' + nick + '"}'
|
||||
);
|
||||
},
|
||||
unsubscribeChatLobby(lobbyId, follow) {
|
||||
console.info('unsubscribe lobby', lobbyId);
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/unsubscribeChatLobby',
|
||||
{},
|
||||
() => ChatRoomsModel.loadSubscribedRooms(follow),
|
||||
true,
|
||||
{},
|
||||
JSON.parse,
|
||||
() => '{"lobby_id":' + lobbyId + '}'
|
||||
);
|
||||
},
|
||||
chatId(action) {
|
||||
return { type: 3, lobby_id: { xstr64: m.route.param('lobby') } };
|
||||
},
|
||||
loadLobby(currentlobbyid) {
|
||||
loadLobbyDetails(currentlobbyid, (detail) => {
|
||||
this.setupAction = this.setIdentity;
|
||||
this.currentLobby = detail;
|
||||
this.isSubscribed = true;
|
||||
this.lobby_user = rs.userList.username(detail.gxs_id) || '???';
|
||||
const lobbyid = currentlobbyid;
|
||||
// apply existing messages to current lobby view
|
||||
rs.events[15].chatMessages(
|
||||
this.chatId(),
|
||||
rs.events[15],
|
||||
(l) => (this.messages = l.map((msg) => m(Message, msg)))
|
||||
);
|
||||
// register for chatEvents for future messages
|
||||
rs.events[15].notify = (chatMessage) => {
|
||||
if (chatMessage.chat_id.type === 3 && chatMessage.chat_id.lobby_id.xstr64 === lobbyid) {
|
||||
this.messages.push(m(Message, chatMessage));
|
||||
m.redraw();
|
||||
}
|
||||
};
|
||||
// lookup for chat-user names (only snapshot, we don't get notified about changes of participants)
|
||||
const names = detail.gxs_ids.reduce((a, u) => a.concat(rs.userList.username(u.key)), []);
|
||||
names.sort((a, b) => a.localeCompare(b));
|
||||
this.users = [];
|
||||
names.forEach((name) => (this.users = this.users.concat([m('.user', name)])));
|
||||
return this.users;
|
||||
});
|
||||
},
|
||||
loadPublicLobby(currentlobbyid) {
|
||||
console.info('loadPublicLobby ChatRoomsModel:', ChatRoomsModel);
|
||||
this.setupAction = this.enterPublicLobby;
|
||||
this.isSubscribed = false;
|
||||
ChatRoomsModel.allRooms.forEach((it) => {
|
||||
if (it.lobby_id.xstr64 === currentlobbyid) {
|
||||
this.currentLobby = it;
|
||||
this.lobby_user = '???';
|
||||
this.lobbyid = currentlobbyid;
|
||||
}
|
||||
});
|
||||
this.users = [];
|
||||
},
|
||||
sendMessage(msg, onsuccess) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsmsgs/sendChat',
|
||||
{},
|
||||
() => {
|
||||
// adding own message to log
|
||||
rs.events[15].handler(
|
||||
{
|
||||
mChatMessage: {
|
||||
chat_id: this.chatId(),
|
||||
msg,
|
||||
sendTime: new Date().getTime() / 1000,
|
||||
lobby_peer_gxs_id: this.currentLobby.gxs_id,
|
||||
},
|
||||
},
|
||||
rs.events[15]
|
||||
);
|
||||
onsuccess();
|
||||
},
|
||||
true,
|
||||
{},
|
||||
undefined,
|
||||
() =>
|
||||
'{"id":{"type": 3,"lobby_id":' +
|
||||
m.route.param('lobby') +
|
||||
'}, "msg":' +
|
||||
JSON.stringify(msg) +
|
||||
'}'
|
||||
);
|
||||
},
|
||||
selected(info, selName, defaultName) {
|
||||
const currid = (ChatLobbyModel.currentLobby.lobby_id || { xstr64: m.route.param('lobby') })
|
||||
.xstr64;
|
||||
return (info.lobby_id.xstr64 === currid ? selName : '') + defaultName;
|
||||
},
|
||||
switchToEvent(info) {
|
||||
return () => {
|
||||
ChatLobbyModel.currentLobby = info;
|
||||
m.route.set('/chat/:lobby', { lobby: info.lobby_id.xstr64 });
|
||||
ChatLobbyModel.loadLobby(info.lobby_id.xstr64); // update
|
||||
};
|
||||
},
|
||||
setupEvent(info) {
|
||||
return () => {
|
||||
m.route.set('/chat/:lobby/setup', { lobby: info.lobby_id.xstr64 });
|
||||
ChatLobbyModel.loadPublicLobby(info.lobby_id.xstr64); // update
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// ************************* views ****************************
|
||||
|
||||
const Lobby = () => {
|
||||
let info = {};
|
||||
let tagname = '';
|
||||
let onclick = e => {};
|
||||
let onclick = (e) => {};
|
||||
let lobbytagname = '';
|
||||
return {
|
||||
oninit: (v) => {
|
||||
info = v.attrs.info;
|
||||
tagname = v.attrs.tagname;
|
||||
onclick = v.attrs.onclick || (e => {});
|
||||
lobbytagname= v.attrs.lobbytagname || 'mainname';
|
||||
info = v.attrs.info;
|
||||
tagname = v.attrs.tagname;
|
||||
onclick = v.attrs.onclick || ((e) => {});
|
||||
lobbytagname = v.attrs.lobbytagname || 'mainname';
|
||||
},
|
||||
view: v => {
|
||||
return m(ChatLobbyModel.selected(info, '.selected-lobby', tagname), {
|
||||
key: info.lobby_id.xstr64,
|
||||
onclick: onclick,
|
||||
}, [
|
||||
m('h5', {class: lobbytagname},info.lobby_name === '' ? '<unnamed>' : info.lobby_name),
|
||||
m('.topic', info.lobby_topic),
|
||||
]);
|
||||
view: (v) => {
|
||||
return m(
|
||||
ChatLobbyModel.selected(info, '.selected-lobby', tagname),
|
||||
{
|
||||
key: info.lobby_id.xstr64,
|
||||
onclick,
|
||||
},
|
||||
[
|
||||
m('h5', { class: lobbytagname }, info.lobby_name === '' ? '<unnamed>' : info.lobby_name),
|
||||
m('.topic', info.lobby_topic),
|
||||
]
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const LobbyList = {
|
||||
view(vnode) {
|
||||
let tagname = vnode.attrs.tagname
|
||||
let lobbytagname = vnode.attrs.lobbytagname
|
||||
let onclick = vnode.attrs.onclick||(() => null)
|
||||
const tagname = vnode.attrs.tagname;
|
||||
const lobbytagname = vnode.attrs.lobbytagname;
|
||||
const onclick = vnode.attrs.onclick || (() => null);
|
||||
return [
|
||||
m('hr'),
|
||||
vnode.attrs.rooms.map(info => m(Lobby, {
|
||||
info,
|
||||
tagname,
|
||||
lobbytagname,
|
||||
onclick: onclick(info),
|
||||
}))
|
||||
vnode.attrs.rooms.map((info) =>
|
||||
m(Lobby, {
|
||||
info,
|
||||
tagname,
|
||||
lobbytagname,
|
||||
onclick: onclick(info),
|
||||
})
|
||||
),
|
||||
];
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const SubscribedLeftLobbies = {
|
||||
view() {
|
||||
@ -276,11 +316,11 @@ const SubscribedLeftLobbies = {
|
||||
m(LobbyList, {
|
||||
rooms: sortLobbies(Object.values(ChatRoomsModel.subscribedRooms)),
|
||||
tagname: '.leftlobby.subscribed',
|
||||
lobbytagname:'leftname',
|
||||
lobbytagname: 'leftname',
|
||||
onclick: ChatLobbyModel.switchToEvent,
|
||||
})
|
||||
]
|
||||
}
|
||||
}),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const SubscribedLobbies = {
|
||||
@ -289,62 +329,74 @@ const SubscribedLobbies = {
|
||||
m('h3', 'Subscribed chat rooms'),
|
||||
m(LobbyList, {
|
||||
rooms: sortLobbies(Object.values(ChatRoomsModel.subscribedRooms)),
|
||||
tagname:'.lobby.subscribed',
|
||||
tagname: '.lobby.subscribed',
|
||||
onclick: ChatLobbyModel.switchToEvent,
|
||||
}),
|
||||
])
|
||||
}
|
||||
}
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
||||
const PublicLeftLobbies = {
|
||||
view() {
|
||||
return [
|
||||
m('h5.lefttitle', 'public:'),
|
||||
m(LobbyList, {
|
||||
rooms: Object.values(ChatRoomsModel.allRooms).filter(info => !ChatRoomsModel.subscribed(info)),
|
||||
rooms: Object.values(ChatRoomsModel.allRooms).filter(
|
||||
(info) => !ChatRoomsModel.subscribed(info)
|
||||
),
|
||||
tagname: '.leftlobby.public',
|
||||
lobbytagname: 'leftname',
|
||||
onclick: ChatLobbyModel.setupEvent,
|
||||
}),
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const PublicLobbies = () => {
|
||||
return m('.widget', [
|
||||
m('h3', 'Public chat rooms'),
|
||||
m(LobbyList, {
|
||||
rooms: ChatRoomsModel.allRooms.filter(info => !ChatRoomsModel.subscribed(info)),
|
||||
rooms: ChatRoomsModel.allRooms.filter((info) => !ChatRoomsModel.subscribed(info)),
|
||||
tagname: '.lobby.public',
|
||||
onclick: ChatLobbyModel.setupEvent,
|
||||
}),
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
const LobbyName = () => {
|
||||
return m('h3.lobbyName',
|
||||
ChatLobbyModel.isSubscribed ? [
|
||||
m('span.chatusername',ChatLobbyModel.lobby_user),
|
||||
m('span.chatatchar','@'),
|
||||
] : [],
|
||||
m('span.chatlobbyname',ChatLobbyModel.currentLobby.lobby_name),
|
||||
m.route.param('subaction') !== 'setup' ? [
|
||||
m('i.fas.fa-cog.setupicon', {
|
||||
title: 'configure lobby',
|
||||
onclick:() => m.route.set('/chat/:lobby/:subaction', {
|
||||
lobby: m.route.param('lobby'),
|
||||
subaction: 'setup'
|
||||
}, { replace: true })
|
||||
})
|
||||
] : [],
|
||||
ChatLobbyModel.isSubscribed ? [
|
||||
m('i.fas.fa-sign-out-alt.leaveicon', {
|
||||
title: 'leaving lobby',
|
||||
onclick:() => ChatLobbyModel.unsubscribeChatLobby(m.route.param('lobby'), () => {
|
||||
m.route.set('/chat', null, { replace: true });
|
||||
})
|
||||
})
|
||||
] : []
|
||||
return m(
|
||||
'h3.lobbyName',
|
||||
ChatLobbyModel.isSubscribed
|
||||
? [m('span.chatusername', ChatLobbyModel.lobby_user), m('span.chatatchar', '@')]
|
||||
: [],
|
||||
m('span.chatlobbyname', ChatLobbyModel.currentLobby.lobby_name),
|
||||
m.route.param('subaction') !== 'setup'
|
||||
? [
|
||||
m('i.fas.fa-cog.setupicon', {
|
||||
title: 'configure lobby',
|
||||
onclick: () =>
|
||||
m.route.set(
|
||||
'/chat/:lobby/:subaction',
|
||||
{
|
||||
lobby: m.route.param('lobby'),
|
||||
subaction: 'setup',
|
||||
},
|
||||
{ replace: true }
|
||||
),
|
||||
}),
|
||||
]
|
||||
: [],
|
||||
ChatLobbyModel.isSubscribed
|
||||
? [
|
||||
m('i.fas.fa-sign-out-alt.leaveicon', {
|
||||
title: 'leaving lobby',
|
||||
onclick: () =>
|
||||
ChatLobbyModel.unsubscribeChatLobby(m.route.param('lobby'), () => {
|
||||
m.route.set('/chat', null, { replace: true });
|
||||
}),
|
||||
}),
|
||||
]
|
||||
: []
|
||||
);
|
||||
};
|
||||
|
||||
@ -352,61 +404,60 @@ const LobbyName = () => {
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: () => m('.tab-page', [
|
||||
m(SubscribedLobbies),
|
||||
PublicLobbies(),
|
||||
])
|
||||
}
|
||||
view: () => m('.tab-page', [m(SubscribedLobbies), PublicLobbies()]),
|
||||
};
|
||||
};
|
||||
|
||||
const LayoutSingle = () => {
|
||||
return {
|
||||
oninit: () => ChatLobbyModel.loadLobby(m.route.param('lobby')),
|
||||
view: vnode => m('.tab-page', [
|
||||
LobbyName(),
|
||||
m('.lobbies',
|
||||
m(SubscribedLeftLobbies),
|
||||
m(PublicLeftLobbies),
|
||||
),
|
||||
m('.messages', ChatLobbyModel.messages),
|
||||
m('.rightbar', ChatLobbyModel.users),
|
||||
m('.chatMessage', {}, m("textarea.chatMsg", {
|
||||
placeholder: 'enter new message and press return to send',
|
||||
onkeydown: e => {
|
||||
if (e.code==='Enter') {
|
||||
var msg = e.target.value;
|
||||
view: (vnode) =>
|
||||
m('.tab-page', [
|
||||
LobbyName(),
|
||||
m('.lobbies', m(SubscribedLeftLobbies), m(PublicLeftLobbies)),
|
||||
m('.messages', ChatLobbyModel.messages),
|
||||
m('.rightbar', ChatLobbyModel.users),
|
||||
m(
|
||||
'.chatMessage',
|
||||
{},
|
||||
m('textarea.chatMsg', {
|
||||
placeholder: 'enter new message and press return to send',
|
||||
onkeydown: (e) => {
|
||||
if (e.code === 'Enter') {
|
||||
const msg = e.target.value;
|
||||
e.target.value = ' sending ... ';
|
||||
ChatLobbyModel.sendMessage(msg, () => e.target.value='');
|
||||
ChatLobbyModel.sendMessage(msg, () => (e.target.value = ''));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})
|
||||
),
|
||||
]),
|
||||
}
|
||||
},
|
||||
})
|
||||
),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
const LayoutSetup = () => {
|
||||
let ownIds = [];
|
||||
return {
|
||||
oninit: () => people_util.ownIds(data => ownIds = data),
|
||||
view: vnode => m('.tab-page', [
|
||||
LobbyName(),
|
||||
m('.lobbies',
|
||||
m(SubscribedLeftLobbies),
|
||||
m(PublicLeftLobbies),
|
||||
),
|
||||
m('.setup', [
|
||||
m('h5.selectidentity','Select identity to use'),
|
||||
ownIds.map(nick => m(
|
||||
'.identity' + (ChatLobbyModel.currentLobby.gxs_id === nick ? '.selectedidentity' : ''),
|
||||
{
|
||||
onclick: () => ChatLobbyModel.setupAction(m.route.param('lobby'), nick)
|
||||
},
|
||||
rs.userList.username(nick),
|
||||
)),
|
||||
oninit: () => peopleUtil.ownIds((data) => (ownIds = data)),
|
||||
view: (vnode) =>
|
||||
m('.tab-page', [
|
||||
LobbyName(),
|
||||
m('.lobbies', m(SubscribedLeftLobbies), m(PublicLeftLobbies)),
|
||||
m('.setup', [
|
||||
m('h5.selectidentity', 'Select identity to use'),
|
||||
ownIds.map((nick) =>
|
||||
m(
|
||||
'.identity' +
|
||||
(ChatLobbyModel.currentLobby.gxs_id === nick ? '.selectedidentity' : ''),
|
||||
{
|
||||
onclick: () => ChatLobbyModel.setupAction(m.route.param('lobby'), nick),
|
||||
},
|
||||
rs.userList.username(nick)
|
||||
)
|
||||
),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
@ -421,32 +472,42 @@ const LayoutSetup = () => {
|
||||
const LayoutCreateDistant = () => {
|
||||
let ownIds = [];
|
||||
return {
|
||||
oninit: () => people_util.ownIds(data => ownIds = data),
|
||||
view: vnode => m('.tab-page', [
|
||||
m('.createDistantChat', [
|
||||
'choose identitiy to chat with ',
|
||||
rs.userList.username(m.route.param('lobby')),
|
||||
ownIds.map(id => m('.identity', {
|
||||
onclick: () => rs.rsJsonApiRequest(
|
||||
'/rsMsgs/initiateDistantChatConnexion',{
|
||||
to_pid:m.route.param('lobby'),
|
||||
from_pid:id,
|
||||
notify: true
|
||||
}, result => {
|
||||
console.info('initiateDistantChatConnexion', result);
|
||||
m.route.set('/chat/:lobbyid',{lobbyid:result.pid});
|
||||
}
|
||||
)
|
||||
}, rs.userList.username(id))),
|
||||
oninit: () => peopleUtil.ownIds((data) => (ownIds = data)),
|
||||
view: (vnode) =>
|
||||
m('.tab-page', [
|
||||
m('.createDistantChat', [
|
||||
'choose identitiy to chat with ',
|
||||
rs.userList.username(m.route.param('lobby')),
|
||||
ownIds.map((id) =>
|
||||
m(
|
||||
'.identity',
|
||||
{
|
||||
onclick: () =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/initiateDistantChatConnexion',
|
||||
{
|
||||
to_pid: m.route.param('lobby'),
|
||||
from_pid: id,
|
||||
notify: true,
|
||||
},
|
||||
(result) => {
|
||||
console.info('initiateDistantChatConnexion', result);
|
||||
m.route.set('/chat/:lobbyid', { lobbyid: result.pid });
|
||||
}
|
||||
),
|
||||
},
|
||||
rs.userList.username(id)
|
||||
)
|
||||
),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
oninit: () => {
|
||||
ChatRoomsModel.loadSubscribedRooms();
|
||||
ChatRoomsModel.loadPublicRooms();
|
||||
ChatRoomsModel.loadSubscribedRooms();
|
||||
ChatRoomsModel.loadPublicRooms();
|
||||
},
|
||||
view: (vnode) => {
|
||||
if (m.route.param('lobby') === undefined) {
|
||||
@ -458,5 +519,5 @@ module.exports = {
|
||||
} else {
|
||||
return m(LayoutSingle);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -6,105 +6,131 @@ const SharedDirectories = () => {
|
||||
return {
|
||||
dirs: [],
|
||||
oninit: (vnode) => {
|
||||
rs.rsJsonApiRequest('/rsFiles/getSharedDirectories', {},
|
||||
(data) => directories = data.dirs);
|
||||
rs.rsJsonApiRequest('/rsFiles/getSharedDirectories', {}, (data) => (directories = data.dirs));
|
||||
},
|
||||
view: (vnode) => m('.widget', [
|
||||
m('h3', 'Shared Directories'),
|
||||
m('hr'),
|
||||
directories.map(dir => m('input[type=text].stretched', {
|
||||
value: dir.filename
|
||||
}))
|
||||
]),
|
||||
}
|
||||
view: (vnode) =>
|
||||
m('.widget', [
|
||||
m('h3', 'Shared Directories'),
|
||||
m('hr'),
|
||||
directories.map((dir) =>
|
||||
m('input[type=text].stretched', {
|
||||
value: dir.filename,
|
||||
})
|
||||
),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
const DownloadDirectory = () => {
|
||||
let dlDir = '';
|
||||
const setDir = () => {
|
||||
const path = document.getElementById('dl-dir-input').value;
|
||||
rsJsonApiRequest('rsFiles/setDownloadDirectory', {
|
||||
path: dlDir,
|
||||
}, () => {});
|
||||
}
|
||||
// const path = document.getElementById('dl-dir-input').value; // unused?
|
||||
rs.rsJsonApiRequest(
|
||||
'rsFiles/setDownloadDirectory',
|
||||
{
|
||||
path: dlDir,
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
};
|
||||
return {
|
||||
oninit: (vnode) => {
|
||||
rs.rsJsonApiRequest('/rsFiles/getDownloadDirectory', {},
|
||||
(data) => dlDir = data.retval);
|
||||
rs.rsJsonApiRequest('/rsFiles/getDownloadDirectory', {}, (data) => (dlDir = data.retval));
|
||||
},
|
||||
view: (vnode) => m('.widget', [
|
||||
m('h3', 'Downloads Directory'),
|
||||
m('hr'),
|
||||
m('input[type=text].stretched#dl-dir-input', {
|
||||
oninput: (e) => dlDir = e.target.value,
|
||||
value: dlDir,
|
||||
onchange: setDir,
|
||||
}),
|
||||
]),
|
||||
}
|
||||
view: (vnode) =>
|
||||
m('.widget', [
|
||||
m('h3', 'Downloads Directory'),
|
||||
m('hr'),
|
||||
m('input[type=text].stretched#dl-dir-input', {
|
||||
oninput: (e) => (dlDir = e.target.value),
|
||||
value: dlDir,
|
||||
onchange: setDir,
|
||||
}),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
const PartialsDirectory = () => {
|
||||
let partialsDir = '';
|
||||
const setDir = () => {
|
||||
const path = document.getElementById('partial-dir-input').value;
|
||||
rsJsonApiRequest('rsFiles/setPartialsDirectory', {
|
||||
path: partialDir
|
||||
}, () => {});
|
||||
}
|
||||
// const path = document.getElementById('partial-dir-input').value; // unused?
|
||||
rs.rsJsonApiRequest(
|
||||
'rsFiles/setPartialsDirectory',
|
||||
{
|
||||
path: partialsDir,
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
};
|
||||
return {
|
||||
oninit: (vnode) => rs.rsJsonApiRequest('/rsFiles/getPartialsDirectory', {},
|
||||
(data) => partialsDir = data.retval,
|
||||
),
|
||||
view: (vnode) => m('.widget.widget-halfwidth', [
|
||||
m('h3', 'Partials Directory'),
|
||||
m('hr'),
|
||||
m('input[type=text].stretched#partial-dir-input', {
|
||||
oninput: (e) => partialsDir = e.target.value,
|
||||
value: partialsDir,
|
||||
onchange: setDir,
|
||||
}),
|
||||
]),
|
||||
oninit: (vnode) =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/getPartialsDirectory',
|
||||
{},
|
||||
(data) => (partialsDir = data.retval)
|
||||
),
|
||||
view: (vnode) =>
|
||||
m('.widget.widget-halfwidth', [
|
||||
m('h3', 'Partials Directory'),
|
||||
m('hr'),
|
||||
m('input[type=text].stretched#partial-dir-input', {
|
||||
oninput: (e) => (partialsDir = e.target.value),
|
||||
value: partialsDir,
|
||||
onchange: setDir,
|
||||
}),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
const TransferOptions = () => {
|
||||
let strategy = undefined;
|
||||
let diskLimit = undefined;
|
||||
const setChunkStrat = () => rs.rsJsonApiRequest('/rsFiles/setDefaultChunkStrategy', {
|
||||
strategy: Number(strategy)
|
||||
const setChunkStrat = () =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/setDefaultChunkStrategy',
|
||||
{
|
||||
strategy: Number(strategy),
|
||||
},
|
||||
() => {},);
|
||||
const setFreeLimit = () => rs.rsJsonApiRequest('/rsFiles/setFreeDiskSpaceLimit', {
|
||||
() => {}
|
||||
);
|
||||
const setFreeLimit = () =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/setFreeDiskSpaceLimit',
|
||||
{
|
||||
MinimumFreeMB: diskLimit,
|
||||
},
|
||||
() => {},);
|
||||
() => {}
|
||||
);
|
||||
return {
|
||||
oninit: (vnode) => {
|
||||
rs.rsJsonApiRequest('/rsFiles/defaultChunkStrategy', {},
|
||||
(data) => strategy = data.retval)
|
||||
rs.rsJsonApiRequest('/rsFiles/freeDiskSpaceLimit', {},
|
||||
(data) => diskLimit = data.retval)
|
||||
rs.rsJsonApiRequest('/rsFiles/defaultChunkStrategy', {}, (data) => (strategy = data.retval));
|
||||
rs.rsJsonApiRequest('/rsFiles/freeDiskSpaceLimit', {}, (data) => (diskLimit = data.retval));
|
||||
},
|
||||
view: (vnode) => m('.widget.widget-half', [
|
||||
m('h3', 'Transfer options'),
|
||||
m('hr'),
|
||||
m('.grid-2col', [
|
||||
m('p', 'Default chunk strategy:'),
|
||||
m('select[name=strategy]', {
|
||||
oninput: (e) => strategy = e.target.value,
|
||||
value: strategy,
|
||||
onchange: setChunkStrat,
|
||||
}, ['Streaming', 'Random', 'Progressive'].map(
|
||||
(val, i) => m('option[value=' + i + ']', val))),
|
||||
m('p', 'Safety disk space limit(MB):'),
|
||||
m('input[type=number].small', {
|
||||
oninput: (e) => diskLimit = e.target.value,
|
||||
value: diskLimit,
|
||||
onchange: setFreeLimit,
|
||||
}),
|
||||
view: (vnode) =>
|
||||
m('.widget.widget-half', [
|
||||
m('h3', 'Transfer options'),
|
||||
m('hr'),
|
||||
m('.grid-2col', [
|
||||
m('p', 'Default chunk strategy:'),
|
||||
m(
|
||||
'select[name=strategy]',
|
||||
{
|
||||
oninput: (e) => (strategy = e.target.value),
|
||||
value: strategy,
|
||||
onchange: setChunkStrat,
|
||||
},
|
||||
['Streaming', 'Random', 'Progressive'].map((val, i) =>
|
||||
m('option[value=' + i + ']', val)
|
||||
)
|
||||
),
|
||||
m('p', 'Safety disk space limit(MB):'),
|
||||
m('input[type=number].small', {
|
||||
oninput: (e) => (diskLimit = e.target.value),
|
||||
value: diskLimit,
|
||||
onchange: setFreeLimit,
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
@ -115,9 +141,8 @@ const Layout = () => {
|
||||
m(DownloadDirectory),
|
||||
m(PartialsDirectory),
|
||||
m(TransferOptions),
|
||||
]
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
|
||||
|
||||
@ -1,113 +1,135 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
let util = require('config/config_util');
|
||||
const util = require('config/config_util'); // for future use
|
||||
|
||||
|
||||
let SetNwMode = () => {
|
||||
mode = undefined;
|
||||
sslId = undefined;
|
||||
setMode = () => rs.rsJsonApiRequest('/');
|
||||
/* eslint-disable no-unused-vars */ const SetNwMode = () => {
|
||||
let mode = undefined;
|
||||
let sslId = undefined;
|
||||
const setMode = () => rs.rsJsonApiRequest('/');
|
||||
return {
|
||||
oninit: util.getSslId().then(val => {
|
||||
oninit: util.getSslId().then((val) => {
|
||||
sslId = val;
|
||||
rs.rsJsonApiRequest('/');
|
||||
}),
|
||||
view: () => [
|
||||
m('p', 'Network mode:'),
|
||||
m('select', {
|
||||
oninput: (e) => mode = e.target.value,
|
||||
value: mode,
|
||||
onchange: setMode,
|
||||
}, ['Discovery on(recommended)', 'Discovery off'].map(
|
||||
(val, i) => m('option[value=' + (i + 1) + ']',
|
||||
val))),
|
||||
m(
|
||||
'select',
|
||||
{
|
||||
oninput: (e) => (mode = e.target.value),
|
||||
value: mode,
|
||||
onchange: setMode,
|
||||
},
|
||||
['Discovery on(recommended)', 'Discovery off'].map((val, i) =>
|
||||
m('option[value=' + (i + 1) + ']', val)
|
||||
)
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
let SetLimits = () => {
|
||||
const SetLimits = () => {
|
||||
let dlim = undefined;
|
||||
let ulim = undefined;
|
||||
let setMaxRates = () => rs.rsJsonApiRequest('/rsConfig/SetMaxDataRates', {
|
||||
downKb: dlim,
|
||||
upKb: ulim
|
||||
});
|
||||
const setMaxRates = () =>
|
||||
rs.rsJsonApiRequest('/rsConfig/SetMaxDataRates', {
|
||||
downKb: dlim,
|
||||
upKb: ulim,
|
||||
});
|
||||
return {
|
||||
oninit: () => rs.rsJsonApiRequest('/rsConfig/GetMaxDataRates', {}, (data) => {
|
||||
dlim = data.inKb;
|
||||
ulim = data.outKb;
|
||||
}),
|
||||
oninit: () =>
|
||||
rs.rsJsonApiRequest('/rsConfig/GetMaxDataRates', {}, (data) => {
|
||||
dlim = data.inKb;
|
||||
ulim = data.outKb;
|
||||
}),
|
||||
view: () => [
|
||||
m('p',
|
||||
m(
|
||||
'p',
|
||||
util.tooltip(
|
||||
'The download limit covers the whole application. ' +
|
||||
'However, in some situations, such as when transfering ' +
|
||||
'many files at once, the estimated bandwidth becomes ' +
|
||||
'unreliable and the total value reported by Retroshare ' +
|
||||
'might exceed that limit.'
|
||||
), 'Download limit(KB/s):'),
|
||||
'However, in some situations, such as when transfering ' +
|
||||
'many files at once, the estimated bandwidth becomes ' +
|
||||
'unreliable and the total value reported by Retroshare ' +
|
||||
'might exceed that limit.'
|
||||
),
|
||||
'Download limit(KB/s):'
|
||||
),
|
||||
m('input[type=number][name=download]', {
|
||||
value: dlim,
|
||||
oninput: (e) => (dlim = Number(e.target.value)),
|
||||
onchange: setMaxRates,
|
||||
}),
|
||||
m(
|
||||
'input[type=number][name=download]', {
|
||||
value: dlim,
|
||||
oninput: (e) => dlim = Number(e.target.value),
|
||||
onchange: setMaxRates,
|
||||
}),
|
||||
m('p',
|
||||
'p',
|
||||
util.tooltip(
|
||||
'The upload limit covers the entire software. ' +
|
||||
'Too small an upload limit may eventually block ' +
|
||||
'low priority services(forums, channels). ' +
|
||||
'A minimum recommended value is 50KB/s.'
|
||||
), 'Upload limit(KB/s):'),
|
||||
'Too small an upload limit may eventually block ' +
|
||||
'low priority services(forums, channels). ' +
|
||||
'A minimum recommended value is 50KB/s.'
|
||||
),
|
||||
'Upload limit(KB/s):'
|
||||
),
|
||||
m('input[type=number][name=upload]', {
|
||||
value: ulim,
|
||||
oninput: (e) => ulim = Number(e.target.value),
|
||||
oninput: (e) => (ulim = Number(e.target.value)),
|
||||
onchange: setMaxRates,
|
||||
}),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
SetOpMode = () => {
|
||||
opmode = undefined;
|
||||
setmode = () => rs.rsJsonApiRequest('/rsconfig/SetOperatingMode', {
|
||||
opMode: Number(opmode)
|
||||
}, () => {});
|
||||
const SetOpMode = () => {
|
||||
let opmode = undefined;
|
||||
const setmode = () =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsconfig/SetOperatingMode',
|
||||
{
|
||||
opMode: Number(opmode),
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
return {
|
||||
oninit: () => rs.rsJsonApiRequest('/rsConfig/getOperatingMode', {},
|
||||
(data) => opmode = data.retval),
|
||||
oninit: () =>
|
||||
rs.rsJsonApiRequest('/rsConfig/getOperatingMode', {}, (data) => (opmode = data.retval)),
|
||||
view: () => [
|
||||
m('p', 'Operating mode:',
|
||||
m(
|
||||
'p',
|
||||
'Operating mode:',
|
||||
util.tooltip(
|
||||
`No Anon D/L: Switches off file forwarding\n
|
||||
Gaming Mode: 25% standard traffic and TODO: Reduced popups\n
|
||||
Low traffic: 10% standard traffic and TODO: pause all file transfers\n`
|
||||
),
|
||||
)
|
||||
),
|
||||
m(
|
||||
'select',
|
||||
{
|
||||
oninput: (e) => (opmode = e.target.value),
|
||||
value: opmode,
|
||||
onchange: setmode,
|
||||
},
|
||||
['Normal', 'No Anon D/L', 'Gaming', 'Low traffic'].map((val, i) =>
|
||||
m('option[value=' + (i + 1) + ']', val)
|
||||
)
|
||||
),
|
||||
m('select', {
|
||||
oninput: (e) => opmode = e.target.value,
|
||||
value: opmode,
|
||||
onchange: setmode,
|
||||
}, ['Normal', 'No Anon D/L', 'Gaming', 'Low traffic'].map(
|
||||
(val, i) => m('option[value=' + (i + 1) + ']', val))),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
let Component = () => {
|
||||
const Component = () => {
|
||||
return {
|
||||
view: () => m('.widget.widget-half', [
|
||||
m('h3', 'Network Configuration'),
|
||||
m('hr'),
|
||||
m('.grid-2col', [
|
||||
m(SetLimits),
|
||||
m(SetOpMode),
|
||||
//m(SetNwMode),
|
||||
view: () =>
|
||||
m('.widget.widget-half', [
|
||||
m('h3', 'Network Configuration'),
|
||||
m('hr'),
|
||||
m('.grid-2col', [
|
||||
m(SetLimits),
|
||||
m(SetOpMode),
|
||||
// m(SetNwMode),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Component;
|
||||
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
const Node = () => {
|
||||
nodeInfo = {
|
||||
setData: function(data) {
|
||||
const nodeInfo = {
|
||||
setData(data) {
|
||||
Object.assign(nodeInfo, data.status);
|
||||
},
|
||||
};
|
||||
return {
|
||||
oninit: function() {
|
||||
rs.rsJsonApiRequest('/rsConfig/getConfigNetStatus', {}, nodeInfo
|
||||
.setData);
|
||||
oninit() {
|
||||
rs.rsJsonApiRequest('/rsConfig/getConfigNetStatus', {}, nodeInfo.setData);
|
||||
},
|
||||
view: function() {
|
||||
view() {
|
||||
return [
|
||||
m('.widget.widget-half', [
|
||||
m('h3', 'Public Information'),
|
||||
@ -24,14 +23,12 @@ const Node = () => {
|
||||
m('li', 'Port Forwarding: ' + nodeInfo.forwardPort),
|
||||
m('li', 'DHT: ' + nodeInfo.DHTActive),
|
||||
m('li', 'uPnP: ' + nodeInfo.uPnPActive),
|
||||
m('li', 'Local Address: ' + nodeInfo.localAddr +
|
||||
' Port: ' + nodeInfo.localPort),
|
||||
m('li', 'Local Address: ' + nodeInfo.localAddr + ' Port: ' + nodeInfo.localPort),
|
||||
]),
|
||||
]),
|
||||
];
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Node;
|
||||
|
||||
|
||||
@ -6,51 +6,58 @@ const Reputation = () => {
|
||||
let deleteBannedAfter = undefined;
|
||||
return {
|
||||
oninit: (vnode) => {
|
||||
rs.rsJsonApiRequest('/rsIdentity/autoAddFriendIdsAsContact', {},
|
||||
(data) => addFriendIdAsContacts = data.retval);
|
||||
rs.rsJsonApiRequest('/rsIdentity/deleteBannedNodesThreshold', {},
|
||||
(data) => deleteBannedAfter = data.retval);
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsIdentity/autoAddFriendIdsAsContact',
|
||||
{},
|
||||
(data) => (addFriendIdAsContacts = data.retval)
|
||||
);
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsIdentity/deleteBannedNodesThreshold',
|
||||
{},
|
||||
(data) => (deleteBannedAfter = data.retval)
|
||||
);
|
||||
},
|
||||
view: (vnode) => m('.widget', [
|
||||
m('h3', 'Reputation'),
|
||||
m('hr'),
|
||||
m('.grid-2col', [
|
||||
m('p',
|
||||
'Automatically add identities owned by friend nodes to my contacts:'
|
||||
),
|
||||
m('input[type=checkbox]', {
|
||||
checked: addFriendIdAsContacts,
|
||||
oninput: (e) => {
|
||||
addFriendIdAsContacts = e.target.checked;
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsIdentity/setAutoAddFriendIdsAsContact', {
|
||||
enabled: addFriendIdAsContacts
|
||||
}, () => {});
|
||||
},
|
||||
}),
|
||||
m('p',
|
||||
'Delete banned identities after(in days, 0 means indefinitely):'
|
||||
),
|
||||
m('input[type=number]', {
|
||||
oninput: (e) => deleteBannedAfter = e.target.value,
|
||||
value: deleteBannedAfter,
|
||||
onchange: () => rs.rsJsonApiRequest(
|
||||
'/rsIdentity/setDeleteBannedNodesThreshold', {
|
||||
days: deleteBannedAfter
|
||||
}, () => {}),
|
||||
}),
|
||||
view: (vnode) =>
|
||||
m('.widget', [
|
||||
m('h3', 'Reputation'),
|
||||
m('hr'),
|
||||
m('.grid-2col', [
|
||||
m('p', 'Automatically add identities owned by friend nodes to my contacts:'),
|
||||
m('input[type=checkbox]', {
|
||||
checked: addFriendIdAsContacts,
|
||||
oninput: (e) => {
|
||||
addFriendIdAsContacts = e.target.checked;
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsIdentity/setAutoAddFriendIdsAsContact',
|
||||
{
|
||||
enabled: addFriendIdAsContacts,
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
},
|
||||
}),
|
||||
m('p', 'Delete banned identities after(in days, 0 means indefinitely):'),
|
||||
m('input[type=number]', {
|
||||
oninput: (e) => (deleteBannedAfter = e.target.value),
|
||||
value: deleteBannedAfter,
|
||||
onchange: () =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsIdentity/setDeleteBannedNodesThreshold',
|
||||
{
|
||||
days: deleteBannedAfter,
|
||||
},
|
||||
() => {}
|
||||
),
|
||||
}),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: (vnode) => [
|
||||
m(Reputation),
|
||||
]
|
||||
view: (vnode) => [m(Reputation)],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let widget = require('widgets');
|
||||
const m = require('mithril');
|
||||
const widget = require('widgets');
|
||||
|
||||
|
||||
let sections = {
|
||||
const sections = {
|
||||
network: require('config/config_network'),
|
||||
node: require('config/config_node'),
|
||||
services: require('config/config_services'),
|
||||
@ -12,13 +10,14 @@ let sections = {
|
||||
};
|
||||
|
||||
const Layout = {
|
||||
view: vnode => m('.tab-page', [
|
||||
m(widget.Sidebar, {
|
||||
tabs: Object.keys(sections),
|
||||
baseRoute: '/config/',
|
||||
}),
|
||||
m('.node-panel', vnode.children),
|
||||
])
|
||||
view: (vnode) =>
|
||||
m('.tab-page', [
|
||||
m(widget.Sidebar, {
|
||||
tabs: Object.keys(sections),
|
||||
baseRoute: '/config/',
|
||||
}),
|
||||
m('.node-panel', vnode.children),
|
||||
]),
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
@ -27,4 +26,3 @@ module.exports = {
|
||||
return m(Layout, m(sections[tab]));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,64 +1,69 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
let servicesInfo = {
|
||||
const servicesInfo = {
|
||||
list: [],
|
||||
setData: function(data) {
|
||||
setData(data) {
|
||||
servicesInfo.list = data.info.mServiceList;
|
||||
},
|
||||
};
|
||||
|
||||
let Service = () => {
|
||||
const Service = () => {
|
||||
let defaultAllowed = undefined;
|
||||
return {
|
||||
oninit: (v) => rs.rsJsonApiRequest(
|
||||
'/rsServiceControl/getServicePermissions', {
|
||||
serviceId: v.attrs.data.key
|
||||
}, (retval) => defaultAllowed = retval.permissions.mDefaultAllowed),
|
||||
view: (v) => m('tr', {
|
||||
key: v.attrs.data.key,
|
||||
}, [
|
||||
m('td', v.attrs.data.value.mServiceName),
|
||||
m('td', v.attrs.data.value.mServiceType),
|
||||
m('td', v.attrs.data.value.mVersionMajor +
|
||||
'.' +
|
||||
v.attrs.data.value.mVersionMinor),
|
||||
m('td', m('input[type=checkbox]', {
|
||||
checked: defaultAllowed,
|
||||
oninput: (e) => {
|
||||
defaultAllowed = e.target.checked;
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsServiceControl/updateServicePermissions', {
|
||||
serviceId: v.attrs.data.key,
|
||||
permissions: {
|
||||
mDefaultAllowed: defaultAllowed
|
||||
}
|
||||
});
|
||||
oninit: (v) =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsServiceControl/getServicePermissions',
|
||||
{
|
||||
serviceId: v.attrs.data.key,
|
||||
},
|
||||
})),
|
||||
]),
|
||||
(retval) => (defaultAllowed = retval.permissions.mDefaultAllowed)
|
||||
),
|
||||
view: (v) =>
|
||||
m(
|
||||
'tr',
|
||||
{
|
||||
key: v.attrs.data.key,
|
||||
},
|
||||
[
|
||||
m('td', v.attrs.data.value.mServiceName),
|
||||
m('td', v.attrs.data.value.mServiceType),
|
||||
m('td', v.attrs.data.value.mVersionMajor + '.' + v.attrs.data.value.mVersionMinor),
|
||||
m(
|
||||
'td',
|
||||
m('input[type=checkbox]', {
|
||||
checked: defaultAllowed,
|
||||
oninput: (e) => {
|
||||
defaultAllowed = e.target.checked;
|
||||
rs.rsJsonApiRequest('/rsServiceControl/updateServicePermissions', {
|
||||
serviceId: v.attrs.data.key,
|
||||
permissions: {
|
||||
mDefaultAllowed: defaultAllowed,
|
||||
},
|
||||
});
|
||||
},
|
||||
})
|
||||
),
|
||||
]
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
let MyServices = {
|
||||
oninit: function() {
|
||||
rs.rsJsonApiRequest('/rsServiceControl/getOwnServices', {},
|
||||
servicesInfo.setData);
|
||||
const MyServices = {
|
||||
oninit() {
|
||||
rs.rsJsonApiRequest('/rsServiceControl/getOwnServices', {}, servicesInfo.setData);
|
||||
},
|
||||
view: function() {
|
||||
view() {
|
||||
return m('.widget', [
|
||||
m('h3', 'My Services'),
|
||||
m('hr'),
|
||||
m('table', [
|
||||
m('tr', [
|
||||
m('th', 'Name'),
|
||||
m('th', 'ID'),
|
||||
m('th', 'Version'),
|
||||
m('th', 'Allow by default'),
|
||||
]),
|
||||
servicesInfo.list.map((data) => m(Service, {
|
||||
data,
|
||||
})),
|
||||
m('tr', [m('th', 'Name'), m('th', 'ID'), m('th', 'Version'), m('th', 'Allow by default')]),
|
||||
servicesInfo.list.map((data) =>
|
||||
m(Service, {
|
||||
data,
|
||||
})
|
||||
),
|
||||
]),
|
||||
]);
|
||||
},
|
||||
@ -66,9 +71,7 @@ let MyServices = {
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: vnode => [
|
||||
m(MyServices),
|
||||
],
|
||||
view: (vnode) => [m(MyServices)],
|
||||
};
|
||||
};
|
||||
|
||||
@ -77,4 +80,3 @@ module.exports = {
|
||||
return m(Layout);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,22 +1,17 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
function tooltip(text) {
|
||||
return m('.tooltip', [
|
||||
m('i.fas.fa-info-circle'),
|
||||
m('.tooltiptext', text)
|
||||
]);
|
||||
};
|
||||
return m('.tooltip', [m('i.fas.fa-info-circle'), m('.tooltiptext', text)]);
|
||||
}
|
||||
|
||||
async function getSslId() {
|
||||
let id = '';
|
||||
await rs.rsJsonApiRequest('/rsIdentity/GetOwnSignedIds', {},
|
||||
(data) => id = data.ids[0]);
|
||||
await rs.rsJsonApiRequest('/rsIdentity/GetOwnSignedIds', {}, (data) => (id = data.ids[0]));
|
||||
return id;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tooltip,
|
||||
getSslId,
|
||||
};
|
||||
|
||||
|
||||
@ -1,35 +1,38 @@
|
||||
.file-view {
|
||||
width: 90%;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 2em;
|
||||
padding: 1em;
|
||||
animation: fadein 0.5s;
|
||||
}.file-view > p {
|
||||
margin: 0.5em;
|
||||
font-size: 1.1em;
|
||||
}.file-view > span {
|
||||
margin: 0 1em;
|
||||
font-size: 0.9em;
|
||||
}.file-view > .progressbar {
|
||||
width: 70%;
|
||||
margin-bottom: 5px;
|
||||
}.file-view button {
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
width: 90%;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 2em;
|
||||
padding: 1em;
|
||||
animation: fadein 0.5s;
|
||||
}
|
||||
.file-view > p {
|
||||
margin: 0.5em;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.file-view > span {
|
||||
margin: 0 1em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.file-view > .progressbar {
|
||||
width: 70%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.file-view button {
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
}
|
||||
span > i {
|
||||
margin-right: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.filestat {
|
||||
min-width: 7em;
|
||||
display: inline-block;
|
||||
min-width: 7em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.searchbar {
|
||||
margin: 5px;
|
||||
width: 40%;
|
||||
box-shadow: 0 0 3px #aaa;
|
||||
margin: 5px;
|
||||
width: 40%;
|
||||
box-shadow: 0 0 3px #aaa;
|
||||
}
|
||||
|
||||
|
||||
@ -1,67 +1,61 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let util = require('files/files_util')
|
||||
let widget = require('widgets');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const util = require('files/files_util');
|
||||
const widget = require('widgets');
|
||||
|
||||
|
||||
function updateFileDetail(hash, isNew = false) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileDetails', {
|
||||
hash,
|
||||
hintflags: 16, // RS_FILE_HINTS_DOWNLOAD
|
||||
},
|
||||
(fileStat) => {
|
||||
if(!fileStat.retval) {
|
||||
console.error('Error: Unknown hash in Downloads: ', hash);
|
||||
return;
|
||||
}
|
||||
fileStat.info.isSearched = (isNew ?
|
||||
true :
|
||||
Downloads.statusMap[hash].isSearched);
|
||||
Downloads.statusMap[hash] = fileStat.info;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let Downloads = {
|
||||
const Downloads = {
|
||||
statusMap: {},
|
||||
hashes: [],
|
||||
|
||||
loadHashes() {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileDownloads', {},
|
||||
(d) => Downloads.hashes = d.hashs,
|
||||
);
|
||||
rs.rsJsonApiRequest('/rsFiles/FileDownloads', {}, (d) => (Downloads.hashes = d.hashs));
|
||||
},
|
||||
|
||||
loadStatus() {
|
||||
Downloads.loadHashes();
|
||||
let fileKeys = Object.keys(Downloads.statusMap);
|
||||
if(Downloads.hashes.length !== fileKeys.length) {
|
||||
const fileKeys = Object.keys(Downloads.statusMap);
|
||||
if (Downloads.hashes.length !== fileKeys.length) {
|
||||
// New file added
|
||||
if(Downloads.hashes.length > fileKeys.length) {
|
||||
let newHashes = util.compareArrays(Downloads.hashes, fileKeys);
|
||||
for(let hash of newHashes) {
|
||||
updateFileDetail(hash, true);
|
||||
if (Downloads.hashes.length > fileKeys.length) {
|
||||
const newHashes = util.compareArrays(Downloads.hashes, fileKeys);
|
||||
for (const hash of newHashes) {
|
||||
Downloads.updateFileDetail(hash, true);
|
||||
}
|
||||
}
|
||||
// Existing file removed
|
||||
else {
|
||||
let oldHashes = util.compareArrays(fileKeys, Downloads.hashes);
|
||||
for(let hash of oldHashes) {
|
||||
const oldHashes = util.compareArrays(fileKeys, Downloads.hashes);
|
||||
for (const hash of oldHashes) {
|
||||
delete Downloads.statusMap[hash];
|
||||
}
|
||||
}
|
||||
}
|
||||
for(let hash in Downloads.statusMap) {
|
||||
updateFileDetail(hash);
|
||||
for (const hash in Downloads.statusMap) {
|
||||
Downloads.updateFileDetail(hash);
|
||||
}
|
||||
},
|
||||
resetSearch() {
|
||||
for(let hash in Downloads.statusMap) {
|
||||
for (const hash in Downloads.statusMap) {
|
||||
Downloads.statusMap[hash].isSearched = true;
|
||||
}
|
||||
},
|
||||
updateFileDetail(hash, isNew = false) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileDetails',
|
||||
{
|
||||
hash,
|
||||
hintflags: 16, // RS_FILE_HINTS_DOWNLOAD
|
||||
},
|
||||
(fileStat) => {
|
||||
if (!fileStat.retval) {
|
||||
console.error('Error: Unknown hash in Downloads: ', hash);
|
||||
return;
|
||||
}
|
||||
fileStat.info.isSearched = isNew ? true : Downloads.statusMap[hash].isSearched;
|
||||
Downloads.statusMap[hash] = fileStat.info;
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
function InvalidFileMessage() {
|
||||
@ -75,33 +69,39 @@ function InvalidFileMessage() {
|
||||
|
||||
function addFile(url) {
|
||||
// valid url format: retroshare://file?name=...&size=...&hash=...
|
||||
if(!url.startsWith('retroshare://')) {
|
||||
if (!url.startsWith('retroshare://')) {
|
||||
InvalidFileMessage();
|
||||
return;
|
||||
}
|
||||
let details = m.parseQueryString(url.split('?')[1]);
|
||||
if(!details.hasOwnProperty('name') ||
|
||||
!details.hasOwnProperty('size') ||
|
||||
!details.hasOwnProperty('hash')) {
|
||||
const details = m.parseQueryString(url.split('?')[1]);
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(details, 'name') ||
|
||||
!Object.prototype.hasOwnProperty.call(details, 'size') ||
|
||||
!Object.prototype.hasOwnProperty.call(details, 'hash')
|
||||
) {
|
||||
InvalidFileMessage();
|
||||
return;
|
||||
}
|
||||
rs.rsJsonApiRequest('/rsFiles/FileRequest', {
|
||||
fileName: details.name,
|
||||
hash: details.hash,
|
||||
flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING,
|
||||
size: Number.parseInt(details.size),
|
||||
}, (status) => {
|
||||
widget.popupMessage([
|
||||
m('i.fas.fa-file-medical'),
|
||||
m('h3', 'Add new file'),
|
||||
m('hr'),
|
||||
m('p', 'Successfully added file!'),
|
||||
]);
|
||||
})
|
||||
};
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileRequest',
|
||||
{
|
||||
fileName: details.name,
|
||||
hash: details.hash,
|
||||
flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING,
|
||||
size: Number.parseInt(details.size),
|
||||
},
|
||||
(status) => {
|
||||
widget.popupMessage([
|
||||
m('i.fas.fa-file-medical'),
|
||||
m('h3', 'Add new file'),
|
||||
m('hr'),
|
||||
m('p', 'Successfully added file!'),
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
NewFileDialog = () => {
|
||||
const NewFileDialog = () => {
|
||||
let url = '';
|
||||
return {
|
||||
view: () => [
|
||||
@ -110,11 +110,15 @@ NewFileDialog = () => {
|
||||
m('hr'),
|
||||
m('p', 'Enter the file link:'),
|
||||
m('input[type=text][name=fileurl]', {
|
||||
onchange: (e) => url = e.target.value,
|
||||
onchange: (e) => (url = e.target.value),
|
||||
}),
|
||||
m('button', {
|
||||
onclick: () => addFile(url),
|
||||
}, 'Add'),
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () => addFile(url),
|
||||
},
|
||||
'Add'
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
@ -122,38 +126,42 @@ NewFileDialog = () => {
|
||||
const Component = () => {
|
||||
return {
|
||||
oninit: () => {
|
||||
rs.setBackgroundTask(
|
||||
Downloads.loadStatus,
|
||||
1000,
|
||||
() => {
|
||||
return (m.route.get() === '/files/files')
|
||||
}
|
||||
);
|
||||
rs.setBackgroundTask(Downloads.loadStatus, 1000, () => {
|
||||
return m.route.get() === '/files/files';
|
||||
});
|
||||
Downloads.resetSearch();
|
||||
},
|
||||
view: () => m('.widget', [
|
||||
m('h3', 'Downloads (' + Downloads.hashes.length +' files)'),
|
||||
m('hr'),
|
||||
m('button', {
|
||||
onclick: () => widget.popupMessage(m(NewFileDialog)),
|
||||
}, 'Add new file'),
|
||||
m('button', {
|
||||
onclick: () => rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileClearCompleted'),
|
||||
}, 'Clear completed'),
|
||||
Object.keys(Downloads.statusMap).map(
|
||||
(hash) => m(util.File, {
|
||||
info: Downloads.statusMap[hash],
|
||||
direction: 'down',
|
||||
transferred: Downloads.statusMap[hash].transfered.xint64,
|
||||
parts: [],
|
||||
})),
|
||||
]),
|
||||
view: () =>
|
||||
m('.widget', [
|
||||
m('h3', 'Downloads (' + Downloads.hashes.length + ' files)'),
|
||||
m('hr'),
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () => widget.popupMessage(m(NewFileDialog)),
|
||||
},
|
||||
'Add new file'
|
||||
),
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () => rs.rsJsonApiRequest('/rsFiles/FileClearCompleted'),
|
||||
},
|
||||
'Clear completed'
|
||||
),
|
||||
Object.keys(Downloads.statusMap).map((hash) =>
|
||||
m(util.File, {
|
||||
info: Downloads.statusMap[hash],
|
||||
direction: 'down',
|
||||
transferred: Downloads.statusMap[hash].transfered.xint64,
|
||||
parts: [],
|
||||
})
|
||||
),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Component,
|
||||
list: Downloads.statusMap
|
||||
list: Downloads.statusMap,
|
||||
};
|
||||
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let widget = require('widgets');
|
||||
|
||||
let downloads = require('files/files_downloads');
|
||||
let uploads = require('files/files_uploads');
|
||||
let util = require('files/files_util');
|
||||
let search = require('files/files_search');
|
||||
const m = require('mithril');
|
||||
const widget = require('widgets');
|
||||
|
||||
const downloads = require('files/files_downloads');
|
||||
const uploads = require('files/files_uploads');
|
||||
const util = require('files/files_util');
|
||||
const search = require('files/files_search');
|
||||
|
||||
const MyFiles = () => {
|
||||
return {
|
||||
@ -20,19 +18,20 @@ const MyFiles = () => {
|
||||
};
|
||||
};
|
||||
|
||||
let sections = {
|
||||
const sections = {
|
||||
files: MyFiles,
|
||||
search: search,
|
||||
search,
|
||||
};
|
||||
|
||||
const Layout = {
|
||||
view: (vnode) => m('.tab-page', [
|
||||
m(widget.Sidebar, {
|
||||
tabs: Object.keys(sections),
|
||||
baseRoute: '/files/',
|
||||
}),
|
||||
m('.node-panel', vnode.children),
|
||||
]),
|
||||
view: (vnode) =>
|
||||
m('.tab-page', [
|
||||
m(widget.Sidebar, {
|
||||
tabs: Object.keys(sections),
|
||||
baseRoute: '/files/',
|
||||
}),
|
||||
m('.node-panel', vnode.children),
|
||||
]),
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
@ -41,4 +40,3 @@ module.exports = {
|
||||
return m(Layout, m(sections[tab]));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,37 +1,36 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let util = require('files/files_util')
|
||||
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
let matchString = '';
|
||||
const SearchBar = () => {
|
||||
return {
|
||||
view: () => m('input[type=text][placeholder=search]', {
|
||||
value: matchString,
|
||||
oninput: (e) => matchString = e.target.value,
|
||||
onchange: (e) => {
|
||||
console.log('searching for string: ', matchString);
|
||||
//let source = new EventSource(
|
||||
// 'http://127.0.0.1:9092/rsFiles/turtleSearchRequest', {
|
||||
// withCredentials: true
|
||||
// });
|
||||
rs.rsJsonApiRequest('/rsfiles/turtleSearchRequest', {
|
||||
matchString
|
||||
}, (data, stat) => {
|
||||
console.log('got: ', stat, ' data: ', data);
|
||||
})
|
||||
},
|
||||
}),
|
||||
view: () =>
|
||||
m('input[type=text][placeholder=search]', {
|
||||
value: matchString,
|
||||
oninput: (e) => (matchString = e.target.value),
|
||||
onchange: (e) => {
|
||||
console.log('searching for string: ', matchString);
|
||||
// let source = new EventSource(
|
||||
// 'http://127.0.0.1:9092/rsFiles/turtleSearchRequest', {
|
||||
// withCredentials: true
|
||||
// });
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsfiles/turtleSearchRequest',
|
||||
{
|
||||
matchString,
|
||||
},
|
||||
(data, stat) => {
|
||||
console.log('got: ', stat, ' data: ', data);
|
||||
}
|
||||
);
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: vnode => m('.widget', [
|
||||
m('h3', 'Search'),
|
||||
m('hr'),
|
||||
m(SearchBar),
|
||||
]),
|
||||
view: (vnode) => m('.widget', [m('h3', 'Search'), m('hr'), m(SearchBar)]),
|
||||
};
|
||||
};
|
||||
|
||||
@ -40,4 +39,3 @@ module.exports = {
|
||||
return m(Layout);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,93 +1,89 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let util = require('files/files_util')
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const util = require('files/files_util');
|
||||
|
||||
|
||||
function updateFileDetail(hash, isNew = false) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileDetails', {
|
||||
hash,
|
||||
hintflags: 32, // RS_FILE_HINTS_UPLOAD
|
||||
},
|
||||
(fileStat) => {
|
||||
if(!fileStat.retval) {
|
||||
console.error('Error: Unknown hash in Uploads: ', hash);
|
||||
return;
|
||||
}
|
||||
fileStat.info.isSearched = (isNew ?
|
||||
true :
|
||||
Uploads.statusMap[hash].isSearched);
|
||||
Uploads.statusMap[hash] = fileStat.info;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let Uploads = {
|
||||
const Uploads = {
|
||||
statusMap: {},
|
||||
hashes: [],
|
||||
|
||||
loadHashes() {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileUploads', {},
|
||||
(d) => Uploads.hashes = d.hashs,
|
||||
);
|
||||
rs.rsJsonApiRequest('/rsFiles/FileUploads', {}, (d) => (Uploads.hashes = d.hashs));
|
||||
},
|
||||
|
||||
loadStatus() {
|
||||
Uploads.loadHashes();
|
||||
let fileKeys = Object.keys(Uploads.statusMap);
|
||||
if(Uploads.hashes.length !== fileKeys.length) {
|
||||
const fileKeys = Object.keys(Uploads.statusMap);
|
||||
if (Uploads.hashes.length !== fileKeys.length) {
|
||||
// New file added
|
||||
if(Uploads.hashes.length > fileKeys.length) {
|
||||
let newHashes = util.compareArrays(Uploads.hashes, fileKeys);
|
||||
for(let hash of newHashes) {
|
||||
updateFileDetail(hash, true);
|
||||
if (Uploads.hashes.length > fileKeys.length) {
|
||||
const newHashes = util.compareArrays(Uploads.hashes, fileKeys);
|
||||
for (const hash of newHashes) {
|
||||
Uploads.updateFileDetail(hash, true);
|
||||
}
|
||||
}
|
||||
// Existing file removed
|
||||
else {
|
||||
let oldHashes = util.compareArrays(fileKeys, Uploads.hashes);
|
||||
for(let hash of oldHashes) {
|
||||
const oldHashes = util.compareArrays(fileKeys, Uploads.hashes);
|
||||
for (const hash of oldHashes) {
|
||||
delete Uploads.statusMap[hash];
|
||||
}
|
||||
}
|
||||
}
|
||||
for(let hash in Uploads.statusMap) {
|
||||
updateFileDetail(hash);
|
||||
for (const hash in Uploads.statusMap) {
|
||||
Uploads.updateFileDetail(hash);
|
||||
}
|
||||
},
|
||||
updateFileDetail(hash, isNew = false) {
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsFiles/FileDetails',
|
||||
{
|
||||
hash,
|
||||
hintflags: 32, // RS_FILE_HINTS_UPLOAD
|
||||
},
|
||||
(fileStat) => {
|
||||
if (!fileStat.retval) {
|
||||
console.error('Error: Unknown hash in Uploads: ', hash);
|
||||
return;
|
||||
}
|
||||
fileStat.info.isSearched = isNew ? true : Uploads.statusMap[hash].isSearched;
|
||||
Uploads.statusMap[hash] = fileStat.info;
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
function averageOf(peers){
|
||||
return peers.reduce( (s,e) => s + e.transfered.xint64, 0) / peers.length;
|
||||
function averageOf(peers) {
|
||||
return peers.reduce((s, e) => s + e.transfered.xint64, 0) / peers.length;
|
||||
}
|
||||
|
||||
const Component = () => {
|
||||
return {
|
||||
oninit: () => rs.setBackgroundTask(
|
||||
Uploads.loadStatus,
|
||||
1000,
|
||||
() => {
|
||||
return (m.route.get() === '/files/files')
|
||||
}
|
||||
),
|
||||
view: () => (Uploads.hashes.length > 0 ? m('.widget', [
|
||||
m('h3', 'Uploads ('+ Uploads.hashes.length +' files)'),
|
||||
m('hr'),
|
||||
Object.keys(Uploads.statusMap).map(
|
||||
(hash) => (m(util.File, {
|
||||
info: Uploads.statusMap[hash],
|
||||
direction: 'up',
|
||||
transferred: averageOf(Uploads.statusMap[hash].peers),
|
||||
parts: Uploads.statusMap[hash].peers.reduce((a,e) => [...a, e.transfered.xint64], []),
|
||||
}))
|
||||
),
|
||||
]) : [])
|
||||
oninit: () =>
|
||||
rs.setBackgroundTask(Uploads.loadStatus, 1000, () => {
|
||||
return m.route.get() === '/files/files';
|
||||
}),
|
||||
view: () =>
|
||||
Uploads.hashes.length > 0
|
||||
? m('.widget', [
|
||||
m('h3', 'Uploads (' + Uploads.hashes.length + ' files)'),
|
||||
m('hr'),
|
||||
Object.keys(Uploads.statusMap).map((hash) =>
|
||||
m(util.File, {
|
||||
info: Uploads.statusMap[hash],
|
||||
direction: 'up',
|
||||
transferred: averageOf(Uploads.statusMap[hash].peers),
|
||||
parts: Uploads.statusMap[hash].peers.reduce(
|
||||
(a, e) => [...a, e.transfered.xint64],
|
||||
[]
|
||||
),
|
||||
})
|
||||
),
|
||||
])
|
||||
: [],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Component,
|
||||
list: Uploads.statusMap
|
||||
list: Uploads.statusMap,
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
const RS_FILE_CTRL_PAUSE = 0x00000100;
|
||||
const RS_FILE_CTRL_START = 0x00000200;
|
||||
@ -14,146 +14,191 @@ const FT_STATE_QUEUED = 0x0005;
|
||||
const FT_STATE_PAUSED = 0x0006;
|
||||
const FT_STATE_CHECKING_HASH = 0x0007;
|
||||
|
||||
const RS_FILE_REQ_ANONYMOUS_ROUTING = 0x00000040
|
||||
const RS_FILE_REQ_ANONYMOUS_ROUTING = 0x00000040;
|
||||
|
||||
function makeFriendlyUnit(bytes) {
|
||||
let cnt = bytes;
|
||||
for (s of ['','k','M','G']) {
|
||||
for (const s of ['', 'k', 'M', 'G']) {
|
||||
if (cnt < 1000) {
|
||||
return cnt.toFixed(1) + ' ' + s + 'B';
|
||||
return cnt.toFixed(1) + ' ' + s + 'B';
|
||||
} else {
|
||||
cnt = cnt / 1024;
|
||||
cnt = cnt / 1024;
|
||||
}
|
||||
}
|
||||
return cnt.toFixed(1) + 'TB';
|
||||
}
|
||||
|
||||
function calcRemainingTime(bytes, rate) {
|
||||
if (rate <=0 || bytes < 1) {
|
||||
return '--';
|
||||
} else {
|
||||
let secs = bytes / rate / 1024;
|
||||
if (secs < 60) {
|
||||
return secs.toFixed() + 's';
|
||||
}
|
||||
let mins = secs / 60; secs = secs - Math.floor(mins) * 60;
|
||||
if (mins < 60) {
|
||||
return mins.toFixed() +'m ' + secs.toFixed() + 's';
|
||||
}
|
||||
let hours = mins / 60; mins = mins -Math.floor(hours) * 60;
|
||||
if (hours < 24) {
|
||||
return hours.toFixed() +'h ' + mins.toFixed() + 'm';
|
||||
}
|
||||
let days = hours / 24; hours = hours - Math.floor(days) * 24;
|
||||
return days.toFixed() + 'd ' + hours.toFixed() + 'h';
|
||||
if (rate <= 0 || bytes < 1) {
|
||||
return '--';
|
||||
} else {
|
||||
let secs = bytes / rate / 1024;
|
||||
if (secs < 60) {
|
||||
return secs.toFixed() + 's';
|
||||
}
|
||||
let mins = secs / 60;
|
||||
secs = secs - Math.floor(mins) * 60;
|
||||
if (mins < 60) {
|
||||
return mins.toFixed() + 'm ' + secs.toFixed() + 's';
|
||||
}
|
||||
let hours = mins / 60;
|
||||
mins = mins - Math.floor(hours) * 60;
|
||||
if (hours < 24) {
|
||||
return hours.toFixed() + 'h ' + mins.toFixed() + 'm';
|
||||
}
|
||||
const days = hours / 24;
|
||||
hours = hours - Math.floor(days) * 24;
|
||||
return days.toFixed() + 'd ' + hours.toFixed() + 'h';
|
||||
}
|
||||
}
|
||||
|
||||
function fileAction(hash, action) {
|
||||
let action_header = '';
|
||||
let json_params = {
|
||||
let actionHeader = '';
|
||||
const jsonParams = {
|
||||
hash,
|
||||
flags: 0
|
||||
flags: 0,
|
||||
};
|
||||
switch (action) {
|
||||
case 'cancel':
|
||||
action_header = '/rsFiles/FileCancel';
|
||||
actionHeader = '/rsFiles/FileCancel';
|
||||
break;
|
||||
|
||||
case 'pause':
|
||||
action_header = '/rsFiles/FileControl';
|
||||
json_params.flags = RS_FILE_CTRL_PAUSE;
|
||||
actionHeader = '/rsFiles/FileControl';
|
||||
jsonParams.flags = RS_FILE_CTRL_PAUSE;
|
||||
break;
|
||||
|
||||
case 'resume':
|
||||
action_header = '/rsFiles/FileControl';
|
||||
json_params.flags = RS_FILE_CTRL_START;
|
||||
actionHeader = '/rsFiles/FileControl';
|
||||
jsonParams.flags = RS_FILE_CTRL_START;
|
||||
break;
|
||||
|
||||
case 'force_check':
|
||||
action_header = '/rsFiles/FileControl';
|
||||
json_params.flags = RS_FILE_CTRL_FORCE_CHECK;
|
||||
actionHeader = '/rsFiles/FileControl';
|
||||
jsonParams.flags = RS_FILE_CTRL_FORCE_CHECK;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error('Unknown action in Downloads.control()');
|
||||
return;
|
||||
};
|
||||
rs.rsJsonApiRequest(action_header, json_params, () => {});
|
||||
};
|
||||
}
|
||||
rs.rsJsonApiRequest(actionHeader, jsonParams, () => {});
|
||||
}
|
||||
|
||||
function actionButton(file, action) {
|
||||
switch (action) {
|
||||
case 'resume':
|
||||
return m('button', {
|
||||
title: 'resume',
|
||||
onclick: function() {
|
||||
fileAction(file.hash, 'resume');
|
||||
return m(
|
||||
'button',
|
||||
{
|
||||
title: 'resume',
|
||||
onclick() {
|
||||
fileAction(file.hash, 'resume');
|
||||
},
|
||||
},
|
||||
}, m('i.fas.fa-play'));
|
||||
m('i.fas.fa-play')
|
||||
);
|
||||
|
||||
case 'pause':
|
||||
return m('button', {
|
||||
title: 'pause',
|
||||
onclick: function() {
|
||||
fileAction(file.hash, 'pause');
|
||||
return m(
|
||||
'button',
|
||||
{
|
||||
title: 'pause',
|
||||
onclick() {
|
||||
fileAction(file.hash, 'pause');
|
||||
},
|
||||
},
|
||||
}, m('i.fas.fa-pause'));
|
||||
m('i.fas.fa-pause')
|
||||
);
|
||||
|
||||
case 'cancel':
|
||||
return m('button.red', {
|
||||
title: 'cancel',
|
||||
onclick: function() {
|
||||
fileAction(file.hash, 'cancel');
|
||||
return m(
|
||||
'button.red',
|
||||
{
|
||||
title: 'cancel',
|
||||
onclick() {
|
||||
fileAction(file.hash, 'cancel');
|
||||
},
|
||||
},
|
||||
}, m('i.fas.fa-times'));
|
||||
m('i.fas.fa-times')
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const ProgressBar = () => {
|
||||
return {
|
||||
view: (v) => m('.progressbar', {
|
||||
style: {
|
||||
content: v.attrs.rate + '%'
|
||||
}
|
||||
},
|
||||
m('span.progress-status', {
|
||||
style: {
|
||||
width: v.attrs.rate + '%'
|
||||
}
|
||||
}, v.attrs.rate.toPrecision(3) + '%'))
|
||||
view: (v) =>
|
||||
m(
|
||||
'.progressbar',
|
||||
{
|
||||
style: {
|
||||
content: v.attrs.rate + '%',
|
||||
},
|
||||
},
|
||||
m(
|
||||
'span.progress-status',
|
||||
{
|
||||
style: {
|
||||
width: v.attrs.rate + '%',
|
||||
},
|
||||
},
|
||||
v.attrs.rate.toPrecision(3) + '%'
|
||||
)
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const File = () => {
|
||||
return {
|
||||
view: (v) => m('.file-view', {
|
||||
key: v.attrs.info.hash,
|
||||
style: {
|
||||
display: v.attrs.info.isSearched ? "block" : "none",
|
||||
}
|
||||
}, [
|
||||
m('p', v.attrs.info.fname),
|
||||
(v.attrs.direction === 'up' || v.attrs.info.downloadStatus === FT_STATE_COMPLETE) ? [] : [
|
||||
actionButton(v.attrs.info, 'cancel'),
|
||||
actionButton(v.attrs.info,
|
||||
v.attrs.info.downloadStatus === FT_STATE_PAUSED ?
|
||||
'resume' : 'pause'),
|
||||
],
|
||||
v.attrs.direction === 'up' ? [] :
|
||||
m(ProgressBar, {
|
||||
rate: v.attrs.transferred / v.attrs.info.size.xint64 * 100,
|
||||
}),
|
||||
m('span.filestat', m('i.fas.fa-file'), makeFriendlyUnit(
|
||||
v.attrs.info.size.xint64)),
|
||||
m('span.filestat', m('i.fas.fa-arrow-circle-' + v.attrs.direction),
|
||||
makeFriendlyUnit(v.attrs.info.tfRate * 1024) + '/s'),
|
||||
v.attrs.direction === 'up' ? [] :
|
||||
m('span.filestat', {title: 'time remaining'}, [ m('i.fas.fa-clock'),
|
||||
calcRemainingTime( v.attrs.info.size.xint64 - v.attrs.transferred, v.attrs.info.tfRate)]),
|
||||
m('span.filestat', {title: 'peers'}, [m('i.fas.fa-users'), v.attrs.info.peers.length], v.attrs.parts.reduce((a,e) => [...a, ' - ' + makeFriendlyUnit(e)],[])),
|
||||
]),
|
||||
}
|
||||
view: (v) =>
|
||||
m(
|
||||
'.file-view',
|
||||
{
|
||||
key: v.attrs.info.hash,
|
||||
style: {
|
||||
display: v.attrs.info.isSearched ? 'block' : 'none',
|
||||
},
|
||||
},
|
||||
[
|
||||
m('p', v.attrs.info.fname),
|
||||
v.attrs.direction === 'up' || v.attrs.info.downloadStatus === FT_STATE_COMPLETE
|
||||
? []
|
||||
: [
|
||||
actionButton(v.attrs.info, 'cancel'),
|
||||
actionButton(
|
||||
v.attrs.info,
|
||||
v.attrs.info.downloadStatus === FT_STATE_PAUSED ? 'resume' : 'pause'
|
||||
),
|
||||
],
|
||||
v.attrs.direction === 'up'
|
||||
? []
|
||||
: m(ProgressBar, {
|
||||
rate: (v.attrs.transferred / v.attrs.info.size.xint64) * 100,
|
||||
}),
|
||||
m('span.filestat', m('i.fas.fa-file'), makeFriendlyUnit(v.attrs.info.size.xint64)),
|
||||
m(
|
||||
'span.filestat',
|
||||
m('i.fas.fa-arrow-circle-' + v.attrs.direction),
|
||||
makeFriendlyUnit(v.attrs.info.tfRate * 1024) + '/s'
|
||||
),
|
||||
v.attrs.direction === 'up'
|
||||
? []
|
||||
: m('span.filestat', { title: 'time remaining' }, [
|
||||
m('i.fas.fa-clock'),
|
||||
calcRemainingTime(
|
||||
v.attrs.info.size.xint64 - v.attrs.transferred,
|
||||
v.attrs.info.tfRate
|
||||
),
|
||||
]),
|
||||
m(
|
||||
'span.filestat',
|
||||
{ title: 'peers' },
|
||||
[m('i.fas.fa-users'), v.attrs.info.peers.length],
|
||||
v.attrs.parts.reduce((a, e) => [...a, ' - ' + makeFriendlyUnit(e)], [])
|
||||
),
|
||||
]
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const SearchBar = () => {
|
||||
@ -164,9 +209,8 @@ const SearchBar = () => {
|
||||
value: searchString,
|
||||
oninput: (e) => {
|
||||
searchString = e.target.value.toLowerCase();
|
||||
for(let hash in v.attrs.list) {
|
||||
if(v.attrs.list[hash].fname.toLowerCase().indexOf(
|
||||
searchString) > -1) {
|
||||
for (const hash in v.attrs.list) {
|
||||
if (v.attrs.list[hash].fname.toLowerCase().indexOf(searchString) > -1) {
|
||||
v.attrs.list[hash].isSearched = true;
|
||||
} else {
|
||||
v.attrs.list[hash].isSearched = false;
|
||||
@ -181,10 +225,10 @@ function compareArrays(big, small) {
|
||||
// Use filter on bigger array
|
||||
// Pass `new Set(array_to_compare)` as second param to filter
|
||||
// Source: https://stackoverflow.com/a/40538072/7683374
|
||||
return big.filter(function(val) {
|
||||
return big.filter(function (val) {
|
||||
return !this.has(val);
|
||||
}, new Set(small));
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RS_FILE_CTRL_PAUSE,
|
||||
@ -204,4 +248,3 @@ module.exports = {
|
||||
SearchBar,
|
||||
compareArrays,
|
||||
};
|
||||
|
||||
|
||||
@ -1,233 +1,242 @@
|
||||
body {
|
||||
background-color: azure;
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
margin: 0em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 0.9em;
|
||||
box-sizing: border-box;
|
||||
background-color: azure;
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
margin: 0em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 0.9em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#overlay{
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0.5,0.5,0.5,0.8);
|
||||
#overlay {
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0.5, 0.5, 0.5, 0.8);
|
||||
}
|
||||
|
||||
.paddingbox{
|
||||
padding:2mm;
|
||||
.paddingbox {
|
||||
padding: 2mm;
|
||||
}
|
||||
|
||||
.nav{
|
||||
list-style-type: none;
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
.nav {
|
||||
list-style-type: none;
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
.nav li{
|
||||
display: inline;
|
||||
padding: 0.1em;
|
||||
margin-right: 1em;
|
||||
border-width: 0.1em;
|
||||
border-color: blue;
|
||||
border-bottom-style: solid;
|
||||
cursor: pointer;
|
||||
.nav li {
|
||||
display: inline;
|
||||
padding: 0.1em;
|
||||
margin-right: 1em;
|
||||
border-width: 0.1em;
|
||||
border-color: blue;
|
||||
border-bottom-style: solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
td{
|
||||
padding: 0.3em;
|
||||
border-style: solid;
|
||||
border-width: 0.1em;
|
||||
border-color: black;
|
||||
td {
|
||||
padding: 0.3em;
|
||||
border-style: solid;
|
||||
border-width: 0.1em;
|
||||
border-color: black;
|
||||
}
|
||||
hr {
|
||||
color: black;
|
||||
color: black;
|
||||
}
|
||||
.menu{
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.0em;
|
||||
.menu {
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0em;
|
||||
}
|
||||
.btn{
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
.btn {
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.btn2, .box{
|
||||
border-style: solid;
|
||||
/*border-color: black;*/
|
||||
border-color: black;
|
||||
/*border-width: 1px;*/
|
||||
border-radius: 0mm;
|
||||
padding: 2mm;
|
||||
font-size: 10mm;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2mm;
|
||||
.btn2,
|
||||
.box {
|
||||
border-style: solid;
|
||||
/*border-color: black;*/
|
||||
border-color: black;
|
||||
/*border-width: 1px;*/
|
||||
border-radius: 0mm;
|
||||
padding: 2mm;
|
||||
font-size: 10mm;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
.btn2:hover{
|
||||
background-color: midnightblue;
|
||||
.btn2:hover {
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.btnSmall{
|
||||
border-style: solid;
|
||||
/*border-color: black;*/
|
||||
border-color: black;
|
||||
/*border-width: 1px;*/
|
||||
border-radius: 0mm;
|
||||
padding: 1mm;
|
||||
font-size: 100%;
|
||||
.btnSmall {
|
||||
border-style: solid;
|
||||
/*border-color: black;*/
|
||||
border-color: black;
|
||||
/*border-width: 1px;*/
|
||||
border-radius: 0mm;
|
||||
padding: 1mm;
|
||||
font-size: 100%;
|
||||
|
||||
cursor: pointer;
|
||||
margin-bottom: 0mm;
|
||||
cursor: pointer;
|
||||
margin-bottom: 0mm;
|
||||
}
|
||||
|
||||
.hidden{
|
||||
display:none;
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.noBorderTable{
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
.noBorderTable {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.noBorderTD{
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
vertical-align: center;
|
||||
.noBorderTD {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
.filelink{
|
||||
color: inherit;
|
||||
.filelink {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 10mm;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 1mm;
|
||||
margin-right: 1mm;
|
||||
|
||||
input,textarea{
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 10mm;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 1mm;
|
||||
margin-right: 1mm;
|
||||
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
/* 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;
|
||||
input:hover {
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
textarea#txtNewMsg{
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 100%;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
height:110px;
|
||||
resize: none;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
textarea#txtNewMsg {
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 100%;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
height: 110px;
|
||||
resize: none;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
textarea#certificate{
|
||||
color: black;
|
||||
font-family: courrier;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 100%;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
height:110px;
|
||||
resize: none;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
textarea#certificate {
|
||||
color: black;
|
||||
font-family: courrier;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 100%;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
height: 110px;
|
||||
resize: none;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input#txtMsgKeyword{
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 100%;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 1mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
input#txtMsgKeyword {
|
||||
color: black;
|
||||
font-family: monospace;
|
||||
background-color: azure;
|
||||
border-color: black;
|
||||
font-size: 100%;
|
||||
border-radius: 0mm;
|
||||
border-width: 1mm;
|
||||
padding: 1mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.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+ */
|
||||
.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+ */
|
||||
.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;
|
||||
-webkit-animation-name: logo_splash; /* Chrome, Safari, Opera */
|
||||
-webkit-animation-duration: 3s; /* Chrome, Safari, Opera */
|
||||
animation-name: logo_splash;
|
||||
animation-duration: 3s;
|
||||
text-align: center;
|
||||
#logo_splash {
|
||||
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-name: logo_splash; /* Chrome, Safari, Opera */
|
||||
-webkit-animation-duration: 3s; /* Chrome, Safari, Opera */
|
||||
animation-name: logo_splash;
|
||||
animation-duration: 3s;
|
||||
text-align: center;
|
||||
}
|
||||
/* Chrome, Safari, Opera */
|
||||
@-webkit-keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Standard syntax */
|
||||
@keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,21 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let widget = require('widgets');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const widget = require('widgets');
|
||||
|
||||
let Certificate = () => {
|
||||
const Certificate = () => {
|
||||
let ownCert = '';
|
||||
let short = false;
|
||||
function loadOwnCert(short) {
|
||||
if (short) {
|
||||
rs.rsJsonApiRequest('/rsPeers/GetShortInvite',
|
||||
{formatRadix: true},
|
||||
data => (ownCert = data.invite),
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsPeers/GetShortInvite',
|
||||
{ formatRadix: true },
|
||||
(data) => (ownCert = data.invite)
|
||||
);
|
||||
} else {
|
||||
rs.rsJsonApiRequest('/rsPeers/GetRetroshareInvite',
|
||||
{},
|
||||
data => (ownCert = data.retval),
|
||||
);
|
||||
rs.rsJsonApiRequest('/rsPeers/GetRetroshareInvite', {}, (data) => (ownCert = data.retval));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
oninit() {
|
||||
@ -30,7 +28,8 @@ let Certificate = () => {
|
||||
m('h3', 'Certificate'),
|
||||
m('p', 'Your Retroshare certificate, click to copy'),
|
||||
m('hr'),
|
||||
m('textarea[readonly]',
|
||||
m(
|
||||
'textarea[readonly]',
|
||||
{
|
||||
id: 'certificate',
|
||||
rows: 14,
|
||||
@ -41,26 +40,23 @@ let Certificate = () => {
|
||||
document.execCommand('copy');
|
||||
},
|
||||
},
|
||||
ownCert,
|
||||
ownCert
|
||||
),
|
||||
m('input[type=checkbox]', {
|
||||
checked: short,
|
||||
oninput: e => {
|
||||
oninput: (e) => {
|
||||
short = e.target.checked;
|
||||
loadOwnCert(short);
|
||||
},
|
||||
}), 'Short version',
|
||||
}),
|
||||
'Short version',
|
||||
]);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function invalidCertPrompt() {
|
||||
widget.popupMessage([
|
||||
m('h3', 'Error'),
|
||||
m('hr'),
|
||||
m('p', 'Not a valid Retroshare certificate.'),
|
||||
]);
|
||||
widget.popupMessage([m('h3', 'Error'), m('hr'), m('p', 'Not a valid Retroshare certificate.')]);
|
||||
}
|
||||
|
||||
function confirmAddPrompt(details, cert) {
|
||||
@ -72,40 +68,35 @@ function confirmAddPrompt(details, cert) {
|
||||
m('ul', [
|
||||
m('li', 'Name: ' + details.name),
|
||||
m('li', 'Location: ' + details.location + '(' + details.id + ')'),
|
||||
m('li',
|
||||
details.isHiddenNode ? details.hiddenNodeAddress : details.extAddr,
|
||||
),
|
||||
m('li', details.isHiddenNode ? details.hiddenNodeAddress : details.extAddr),
|
||||
]),
|
||||
m('button',
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsPeers/loadCertificateFromString',
|
||||
{cert},
|
||||
data => {
|
||||
if (data.retval) {
|
||||
widget.popupMessage([
|
||||
m('h3', 'Successful'),
|
||||
m('hr'),
|
||||
m('p', 'Successfully added friend.'),
|
||||
]);
|
||||
} else {
|
||||
widget.popupMessage([
|
||||
m('h3', 'Error'),
|
||||
m('hr'),
|
||||
m('p', 'An error occoured during adding. Friend not added.'),
|
||||
]);
|
||||
}
|
||||
},
|
||||
),
|
||||
rs.rsJsonApiRequest('/rsPeers/loadCertificateFromString', { cert }, (data) => {
|
||||
if (data.retval) {
|
||||
widget.popupMessage([
|
||||
m('h3', 'Successful'),
|
||||
m('hr'),
|
||||
m('p', 'Successfully added friend.'),
|
||||
]);
|
||||
} else {
|
||||
widget.popupMessage([
|
||||
m('h3', 'Error'),
|
||||
m('hr'),
|
||||
m('p', 'An error occoured during adding. Friend not added.'),
|
||||
]);
|
||||
}
|
||||
}),
|
||||
},
|
||||
'Finish',
|
||||
'Finish'
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
function addFriendFromCert(cert) {
|
||||
rs.rsJsonApiRequest('/rsPeers/loadDetailsFromStringCert', {cert}, data => {
|
||||
rs.rsJsonApiRequest('/rsPeers/loadDetailsFromStringCert', { cert }, (data) => {
|
||||
if (!data.retval) {
|
||||
invalidCertPrompt();
|
||||
return null;
|
||||
@ -123,8 +114,8 @@ const AddFriend = () => {
|
||||
// TODO handle incorrect file
|
||||
return null;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
certificate = e.target.result;
|
||||
m.redraw();
|
||||
};
|
||||
@ -132,14 +123,16 @@ const AddFriend = () => {
|
||||
}
|
||||
|
||||
return {
|
||||
view: vnode =>
|
||||
view: (vnode) =>
|
||||
m('.widget.widget-half', [
|
||||
m('h3', 'Add friend'),
|
||||
m('p',
|
||||
'Did you recieve a certificate from a friend? You can also drag and drop the file below',
|
||||
m(
|
||||
'p',
|
||||
'Did you recieve a certificate from a friend? You can also drag and drop the file below'
|
||||
),
|
||||
m('hr'),
|
||||
m('.cert-drop-zone',
|
||||
m(
|
||||
'.cert-drop-zone',
|
||||
{
|
||||
isDragged: false,
|
||||
ondragenter: () => (vnode.state.isDragged = true),
|
||||
@ -152,8 +145,8 @@ const AddFriend = () => {
|
||||
}
|
||||
: {},
|
||||
|
||||
ondragover: e => e.preventDefault(),
|
||||
ondrop: e => {
|
||||
ondragover: (e) => e.preventDefault(),
|
||||
ondrop: (e) => {
|
||||
vnode.state.isDragged = false;
|
||||
e.preventDefault();
|
||||
loadFileContents(e.target.files || e.dataTransfer.files);
|
||||
@ -162,23 +155,24 @@ const AddFriend = () => {
|
||||
|
||||
[
|
||||
m('input[type=file][name=certificate]', {
|
||||
onchange: e => {
|
||||
onchange: (e) => {
|
||||
// Note: this one is for the 'browse' button
|
||||
loadFileContents(e.target.files || e.dataTransfer.files);
|
||||
},
|
||||
}),
|
||||
'Or paste the certificate here',
|
||||
m('textarea[rows=5][style="width: 90%; display: block;"]', {
|
||||
oninput: e => (certificate = e.target.value),
|
||||
oninput: (e) => (certificate = e.target.value),
|
||||
value: certificate,
|
||||
}),
|
||||
m('button',
|
||||
m(
|
||||
'button',
|
||||
{
|
||||
onclick: () => addFriendFromCert(certificate),
|
||||
},
|
||||
'Add',
|
||||
'Add'
|
||||
),
|
||||
],
|
||||
]
|
||||
),
|
||||
]),
|
||||
};
|
||||
@ -191,4 +185,3 @@ const Layout = () => {
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
|
||||
|
||||
@ -1,97 +1,120 @@
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
const displayErrorMessage = function(message) {
|
||||
const displayErrorMessage = function (message) {
|
||||
m.render(document.getElementById('error'), message);
|
||||
};
|
||||
|
||||
const verifyLogin = async function(uname, passwd, url, displayAuthError = true) {
|
||||
const verifyLogin = async function (uname, passwd, url, displayAuthError = true) {
|
||||
const loginHeader = {
|
||||
Authorization: 'Basic ' + btoa(uname + ':' + passwd)
|
||||
Authorization: 'Basic ' + btoa(uname + ':' + passwd),
|
||||
};
|
||||
if (!url.trim()) {
|
||||
displayErrorMessage("Server-url is missing, please enter json-api url");
|
||||
displayErrorMessage('Server-url is missing, please enter json-api url');
|
||||
return;
|
||||
}
|
||||
rs.setKeys('', '', url, false);
|
||||
rs.logon(loginHeader, displayAuthError ? displayErrorMessage : () => {}, displayErrorMessage, () => {
|
||||
rs.setKeys(uname, passwd, url);
|
||||
m.route.set('/home');
|
||||
});
|
||||
rs.logon(
|
||||
loginHeader,
|
||||
displayAuthError ? displayErrorMessage : () => {},
|
||||
displayErrorMessage,
|
||||
() => {
|
||||
rs.setKeys(uname, passwd, url);
|
||||
m.route.set('/home');
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function loginComponent() {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
let uname = urlParams.get('Username')||'webui';
|
||||
let passwd = urlParams.get('Password') ||'';
|
||||
let url = urlParams.get('Url') || window.location.protocol==='file:' ? 'http://127.0.0.1:9092' : window.location.protocol + '//' + window.location.host + window.location.pathname.replace('/index.html','');
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let uname = urlParams.get('Username') || 'webui';
|
||||
let passwd = urlParams.get('Password') || '';
|
||||
let url =
|
||||
urlParams.get('Url') || window.location.protocol === 'file:'
|
||||
? 'http://127.0.0.1:9092'
|
||||
: window.location.protocol +
|
||||
'//' +
|
||||
window.location.host +
|
||||
window.location.pathname.replace('/index.html', '');
|
||||
let withOptions = false;
|
||||
let logo = () => m('img.logo[width=30%]', {
|
||||
src: '../data/retroshare.svg',
|
||||
alt: 'retroshare_icon'
|
||||
});
|
||||
let inputName = () => m('input', {
|
||||
id: 'username',
|
||||
type: 'text',
|
||||
value: uname,
|
||||
placeholder: 'Username',
|
||||
onchange: e => (uname = e.target.value)
|
||||
});
|
||||
let inputPassword = () => m('input[autofocus]', {
|
||||
id: 'password',
|
||||
type: 'password',
|
||||
placeholder: 'Password',
|
||||
onchange: e => (passwd = e.target.value),
|
||||
onkeydown: e => {
|
||||
if (e.keyCode===13) {
|
||||
passwd = e.target.value;
|
||||
loginBtn.click();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
let inputUrl = () => m('input',{
|
||||
id: 'url',
|
||||
type: 'text',
|
||||
placeholder: 'Url',
|
||||
value: url,
|
||||
oninput: e => (url = e.target.value)
|
||||
});
|
||||
const logo = () =>
|
||||
m('img.logo[width=30%]', {
|
||||
src: '../data/retroshare.svg',
|
||||
alt: 'retroshare_icon',
|
||||
});
|
||||
const inputName = () =>
|
||||
m('input', {
|
||||
id: 'username',
|
||||
type: 'text',
|
||||
value: uname,
|
||||
placeholder: 'Username',
|
||||
onchange: (e) => (uname = e.target.value),
|
||||
});
|
||||
const buttonLogin = () =>
|
||||
m(
|
||||
'button.submit-btn',
|
||||
{
|
||||
id: 'loginBtn',
|
||||
onclick: () => verifyLogin(uname, passwd, url),
|
||||
},
|
||||
'Login'
|
||||
);
|
||||
const inputPassword = () =>
|
||||
m('input[autofocus]', {
|
||||
id: 'password',
|
||||
type: 'password',
|
||||
placeholder: 'Password',
|
||||
onchange: (e) => (passwd = e.target.value),
|
||||
onkeydown: (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
passwd = e.target.value;
|
||||
buttonLogin().click();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
const inputUrl = () =>
|
||||
m('input', {
|
||||
id: 'url',
|
||||
type: 'text',
|
||||
placeholder: 'Url',
|
||||
value: url,
|
||||
oninput: (e) => (url = e.target.value),
|
||||
});
|
||||
|
||||
let linkOptions = action => m('a',{
|
||||
onclick: e => withOptions=!withOptions,
|
||||
}, action + ' options');
|
||||
const linkOptions = (action) =>
|
||||
m(
|
||||
'a',
|
||||
{
|
||||
onclick: (e) => (withOptions = !withOptions),
|
||||
},
|
||||
action + ' options'
|
||||
);
|
||||
|
||||
let buttonLogin = () => m('button.submit-btn', {
|
||||
id: 'loginBtn',
|
||||
onclick: () => verifyLogin(uname, passwd, url)
|
||||
}, 'Login');
|
||||
|
||||
let textError = () => m('p.error[id=error]');
|
||||
const textError = () => m('p.error[id=error]');
|
||||
return {
|
||||
oninit: () => verifyLogin(uname, passwd, url, false),
|
||||
view: () => {
|
||||
return m('.login-page',
|
||||
m('.login-container', withOptions
|
||||
? [
|
||||
logo(),
|
||||
m(".extra",[m('label','Username:'),m('br'), inputName()]),
|
||||
m(".extra",[m('label','Password:'),m('br'), inputPassword()]),
|
||||
m(".extra",[m('label','Url:'),m('br'), inputUrl()]),
|
||||
linkOptions('hide'),
|
||||
buttonLogin(),
|
||||
textError()
|
||||
] : [
|
||||
logo(),
|
||||
inputPassword(),
|
||||
linkOptions('show'),
|
||||
buttonLogin(),
|
||||
textError()
|
||||
])
|
||||
return m(
|
||||
'.login-page',
|
||||
m(
|
||||
'.login-container',
|
||||
withOptions
|
||||
? [
|
||||
logo(),
|
||||
m('.extra', [m('label', 'Username:'), m('br'), inputName()]),
|
||||
m('.extra', [m('label', 'Password:'), m('br'), inputPassword()]),
|
||||
m('.extra', [m('label', 'Url:'), m('br'), inputUrl()]),
|
||||
linkOptions('hide'),
|
||||
buttonLogin(),
|
||||
textError(),
|
||||
]
|
||||
: [logo(), inputPassword(), linkOptions('show'), buttonLogin(), textError()]
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = loginComponent;
|
||||
|
||||
@ -1,58 +1,59 @@
|
||||
/* star */
|
||||
table.mails th:nth-child(1) {
|
||||
width: 5%;
|
||||
color: #fcba03;
|
||||
width: 5%;
|
||||
color: #fcba03;
|
||||
}
|
||||
/* attachments */
|
||||
table.mails th:nth-child(2) {
|
||||
width: 5%;
|
||||
color: grey;
|
||||
width: 5%;
|
||||
color: grey;
|
||||
}
|
||||
/* subject */
|
||||
table.mails th:nth-child(3) {
|
||||
width: 50%;
|
||||
text-align: start;
|
||||
width: 50%;
|
||||
text-align: start;
|
||||
}
|
||||
/* date */
|
||||
table.mails th:nth-child(4) {
|
||||
width: 40%;
|
||||
text-align: start;
|
||||
width: 40%;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
/* subject */
|
||||
table.mails td:nth-child(3) {
|
||||
text-align: start;
|
||||
/* Truncate text with '...' */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: start;
|
||||
/* Truncate text with '...' */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* date */
|
||||
table.mails td:nth-child(4) {
|
||||
text-align: start;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
table.mails tr:hover {
|
||||
background-color: #eef3f6;
|
||||
cursor: pointer;
|
||||
background-color: #eef3f6;
|
||||
cursor: pointer;
|
||||
}
|
||||
table.mails tr.unread {
|
||||
color: black;
|
||||
background-color: #eef3f6;
|
||||
color: black;
|
||||
background-color: #eef3f6;
|
||||
}
|
||||
/* hide checkbox */
|
||||
input.star-check {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
/* use label with 'for' to manipulate checkbox */
|
||||
input.star-check + label.star-check {
|
||||
color: grey;
|
||||
} input.star-check:checked + label.star-check {
|
||||
color: #fcba03;
|
||||
color: grey;
|
||||
}
|
||||
input.star-check:checked + label.star-check {
|
||||
color: #fcba03;
|
||||
}
|
||||
|
||||
iframe.msg {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 450px;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
@ -1,24 +1,27 @@
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const util = require('mail/mail_util');
|
||||
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: (v) => [
|
||||
m('.widget', [
|
||||
m('h3', 'Drafts'),
|
||||
m('hr'),
|
||||
m(util.Table, m('tbody', v.attrs.list.map(
|
||||
(msg) => m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'drafts',
|
||||
})
|
||||
))),
|
||||
m(
|
||||
util.Table,
|
||||
m(
|
||||
'tbody',
|
||||
v.attrs.list.map((msg) =>
|
||||
m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'drafts',
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
|
||||
|
||||
@ -1,24 +1,27 @@
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const util = require('mail/mail_util');
|
||||
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: (v) => [
|
||||
m('.widget', [
|
||||
m('h3', 'Inbox'),
|
||||
m('hr'),
|
||||
m(util.Table, m('tbody', v.attrs.list.map(
|
||||
(msg) => m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'inbox',
|
||||
})
|
||||
))),
|
||||
m(
|
||||
util.Table,
|
||||
m(
|
||||
'tbody',
|
||||
v.attrs.list.map((msg) =>
|
||||
m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'inbox',
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
|
||||
|
||||
@ -1,24 +1,27 @@
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const util = require('mail/mail_util');
|
||||
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: (v) => [
|
||||
m('.widget', [
|
||||
m('h3', 'Outbox'),
|
||||
m('hr'),
|
||||
m(util.Table, m('tbody', v.attrs.list.map(
|
||||
(msg) => m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'outbox',
|
||||
})
|
||||
))),
|
||||
m(
|
||||
util.Table,
|
||||
m(
|
||||
'tbody',
|
||||
v.attrs.list.map((msg) =>
|
||||
m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'outbox',
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
let util = require('mail/mail_util');
|
||||
let widget = require('widgets');
|
||||
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const util = require('mail/mail_util');
|
||||
const widget = require('widgets');
|
||||
|
||||
const Messages = {
|
||||
all: [],
|
||||
@ -11,22 +10,25 @@ const Messages = {
|
||||
outbox: [],
|
||||
drafts: [],
|
||||
load() {
|
||||
rs.rsJsonApiRequest('/rsMsgs/getMessageSummaries', {},
|
||||
(data) => {
|
||||
Messages.all = data.msgList
|
||||
Messages.inbox = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_INBOX);
|
||||
Messages.sent = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_SENTBOX);
|
||||
Messages.outbox = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_OUTBOX);
|
||||
Messages.drafts = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_DRAFTBOX);
|
||||
});
|
||||
rs.rsJsonApiRequest('/rsMsgs/getMessageSummaries', {}, (data) => {
|
||||
Messages.all = data.msgList;
|
||||
Messages.inbox = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_INBOX
|
||||
);
|
||||
Messages.sent = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_SENTBOX
|
||||
);
|
||||
Messages.outbox = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_OUTBOX
|
||||
);
|
||||
Messages.drafts = Messages.all.filter(
|
||||
(msg) => (msg.msgflags & util.RS_MSG_BOXMASK) === util.RS_MSG_DRAFTBOX
|
||||
);
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let sections = {
|
||||
const sections = {
|
||||
inbox: require('mail/mail_inbox'),
|
||||
outbox: require('mail/mail_outbox'),
|
||||
drafts: require('mail/mail_draftbox'),
|
||||
@ -35,27 +37,33 @@ let sections = {
|
||||
|
||||
const Layout = {
|
||||
oninit: Messages.load,
|
||||
view: vnode => m('.tab-page', [
|
||||
m(widget.Sidebar, {
|
||||
tabs: Object.keys(sections),
|
||||
baseRoute: '/mail/',
|
||||
}),
|
||||
m('.node-panel', vnode.children),
|
||||
])
|
||||
view: (vnode) =>
|
||||
m('.tab-page', [
|
||||
m(widget.Sidebar, {
|
||||
tabs: Object.keys(sections),
|
||||
baseRoute: '/mail/',
|
||||
}),
|
||||
m('.node-panel', vnode.children),
|
||||
]),
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
view: (v) => {
|
||||
const tab = v.attrs.tab;
|
||||
// TODO: utilize multiple routing params
|
||||
if(v.attrs.hasOwnProperty('msgId')) {
|
||||
return m(Layout, m(util.MessageView, {
|
||||
id: v.attrs.msgId,
|
||||
}))
|
||||
if (Object.prototype.hasOwnProperty.call(v.attrs, 'msgId')) {
|
||||
return m(
|
||||
Layout,
|
||||
m(util.MessageView, {
|
||||
id: v.attrs.msgId,
|
||||
})
|
||||
);
|
||||
}
|
||||
return m(Layout, m(sections[tab], {
|
||||
list: Messages[tab],
|
||||
}));
|
||||
return m(
|
||||
Layout,
|
||||
m(sections[tab], {
|
||||
list: Messages[tab],
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,24 +1,27 @@
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const util = require('mail/mail_util');
|
||||
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: (v) => [
|
||||
m('.widget', [
|
||||
m('h3', 'Sent'),
|
||||
m('hr'),
|
||||
m(util.Table, m('tbody', v.attrs.list.map(
|
||||
(msg) => m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'sent',
|
||||
})
|
||||
))),
|
||||
m(
|
||||
util.Table,
|
||||
m(
|
||||
'tbody',
|
||||
v.attrs.list.map((msg) =>
|
||||
m(util.MessageSummary, {
|
||||
details: msg,
|
||||
category: 'sent',
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const people = require('people/people_util');
|
||||
|
||||
const RS_MSG_BOXMASK = 0x000f;
|
||||
|
||||
@ -13,61 +12,75 @@ const RS_MSG_NEW = 0x10;
|
||||
const RS_MSG_UNREAD_BY_USER = 0x40;
|
||||
const RS_MSG_STAR = 0x200;
|
||||
|
||||
|
||||
const MessageSummary = () => {
|
||||
let details = {};
|
||||
let files = [];
|
||||
let isStarred = undefined;
|
||||
let msgStatus = '';
|
||||
return {
|
||||
oninit: (v) => rs.rsJsonApiRequest('/rsMsgs/getMessage', {
|
||||
msgId: v.attrs.details.msgId,
|
||||
}, (data) => {
|
||||
details = data.msg;
|
||||
files = details.files;
|
||||
isStarred = (details.msgflags & 0xf00) === RS_MSG_STAR;
|
||||
let flag = details.msgflags & 0xf0;
|
||||
if(flag === RS_MSG_NEW || flag == RS_MSG_UNREAD_BY_USER) {
|
||||
msgStatus = 'unread';
|
||||
} else {
|
||||
msgStatus = 'read';
|
||||
}
|
||||
}),
|
||||
view: (v) => m('tr.msgbody', {
|
||||
key: details.msgId,
|
||||
class: msgStatus,
|
||||
onclick: () => m.route.set('/mail/:tab/:msgId', {
|
||||
tab: v.attrs.category,
|
||||
msgId: details.msgId,
|
||||
}),
|
||||
}, [
|
||||
m('td',
|
||||
m('input.star-check[type=checkbox][id=msg-' + details.msgId +
|
||||
']', {
|
||||
checked: v.state.isStarred
|
||||
}),
|
||||
// Use label with [for] to manipulate hidden checkbox
|
||||
m('label.star-check[for=msg-' + details.msgId + ']', {
|
||||
onclick: (e) => {
|
||||
v.state.isStarred = !v.state.isStarred;
|
||||
rs.rsJsonApiRequest('/rsMsgs/MessageStar', {
|
||||
oninit: (v) =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/getMessage',
|
||||
{
|
||||
msgId: v.attrs.details.msgId,
|
||||
},
|
||||
(data) => {
|
||||
details = data.msg;
|
||||
files = details.files;
|
||||
isStarred = (details.msgflags & 0xf00) === RS_MSG_STAR;
|
||||
const flag = details.msgflags & 0xf0;
|
||||
if (flag === RS_MSG_NEW || flag === RS_MSG_UNREAD_BY_USER) {
|
||||
msgStatus = 'unread';
|
||||
} else {
|
||||
msgStatus = 'read';
|
||||
}
|
||||
}
|
||||
),
|
||||
view: (v) =>
|
||||
m(
|
||||
'tr.msgbody',
|
||||
{
|
||||
key: details.msgId,
|
||||
class: msgStatus,
|
||||
onclick: () =>
|
||||
m.route.set('/mail/:tab/:msgId', {
|
||||
tab: v.attrs.category,
|
||||
msgId: details.msgId,
|
||||
mark: v.state.isStarred,
|
||||
});
|
||||
// Stop event bubbling, both functions for supporting IE & FF
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
class: (details.msgflags & 0xf00) === RS_MSG_STAR ?
|
||||
'starred' : 'unstarred'
|
||||
}, m('i.fas.fa-star'))),
|
||||
m('td', files.length),
|
||||
m('td', details.title),
|
||||
//m('td', details.rspeerid_srcId == 0 ?
|
||||
// '[Notification]' :
|
||||
// people.getInfo(details.rspeerid_srcId)),
|
||||
m('td', new Date(details.ts * 1000).toLocaleString()),
|
||||
]),
|
||||
}),
|
||||
},
|
||||
[
|
||||
m(
|
||||
'td',
|
||||
m('input.star-check[type=checkbox][id=msg-' + details.msgId + ']', {
|
||||
checked: isStarred,
|
||||
}),
|
||||
// Use label with [for] to manipulate hidden checkbox
|
||||
m(
|
||||
'label.star-check[for=msg-' + details.msgId + ']',
|
||||
{
|
||||
onclick: (e) => {
|
||||
isStarred = !isStarred;
|
||||
rs.rsJsonApiRequest('/rsMsgs/MessageStar', {
|
||||
msgId: details.msgId,
|
||||
mark: isStarred,
|
||||
});
|
||||
// Stop event bubbling, both functions for supporting IE & FF
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
class: (details.msgflags & 0xf00) === RS_MSG_STAR ? 'starred' : 'unstarred',
|
||||
},
|
||||
m('i.fas.fa-star')
|
||||
)
|
||||
),
|
||||
m('td', files.length),
|
||||
m('td', details.title),
|
||||
// m('td', details.rspeerid_srcId == 0 ?
|
||||
// '[Notification]' :
|
||||
// peopleUtils.getInfo(details.rspeerid_srcId)), // getInfo previously uses "/rsIdentity/getIdentitiesInfo"
|
||||
m('td', new Date(details.ts * 1000).toLocaleString()),
|
||||
]
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@ -75,48 +88,68 @@ const MessageView = () => {
|
||||
let details = {};
|
||||
let message = '';
|
||||
return {
|
||||
oninit: (v) => rs.rsJsonApiRequest('/rsMsgs/getMessage', {
|
||||
msgId: v.attrs.id,
|
||||
}, (data) => {
|
||||
details = data.msg;
|
||||
// regex to detect html tags
|
||||
// better regex? /<[a-z][\s\S]*>/gi
|
||||
if(/<\/*[a-z][^>]+?>/gi.test(details.msg)) {
|
||||
message = details.msg;
|
||||
} else {
|
||||
message = '<p style="white-space: pre">' + details.msg + '</p>';
|
||||
}
|
||||
}),
|
||||
view: (v) => m('.widget.msgview', {
|
||||
key: details.msgId,
|
||||
}, [
|
||||
m('a[title=Back]', {
|
||||
onclick: () => m.route.set('/mail/:tab', {
|
||||
tab: m.route.param().tab,
|
||||
}),
|
||||
}, m('i.fas.fa-arrow-left')),
|
||||
m('h3', details.title),
|
||||
m('hr'),
|
||||
m('iframe[title=message].msg', {
|
||||
srcdoc: message,
|
||||
}, message),
|
||||
]),
|
||||
oninit: (v) =>
|
||||
rs.rsJsonApiRequest(
|
||||
'/rsMsgs/getMessage',
|
||||
{
|
||||
msgId: v.attrs.id,
|
||||
},
|
||||
(data) => {
|
||||
details = data.msg;
|
||||
// regex to detect html tags
|
||||
// better regex? /<[a-z][\s\S]*>/gi
|
||||
if (/<\/*[a-z][^>]+?>/gi.test(details.msg)) {
|
||||
message = details.msg;
|
||||
} else {
|
||||
message = '<p style="white-space: pre">' + details.msg + '</p>';
|
||||
}
|
||||
}
|
||||
),
|
||||
view: (v) =>
|
||||
m(
|
||||
'.widget.msgview',
|
||||
{
|
||||
key: details.msgId,
|
||||
},
|
||||
[
|
||||
m(
|
||||
'a[title=Back]',
|
||||
{
|
||||
onclick: () =>
|
||||
m.route.set('/mail/:tab', {
|
||||
tab: m.route.param().tab,
|
||||
}),
|
||||
},
|
||||
m('i.fas.fa-arrow-left')
|
||||
),
|
||||
m('h3', details.title),
|
||||
m('hr'),
|
||||
m(
|
||||
'iframe[title=message].msg',
|
||||
{
|
||||
srcdoc: message,
|
||||
},
|
||||
message
|
||||
),
|
||||
]
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const Table = () => {
|
||||
return {
|
||||
oninit: (v) => {},
|
||||
view: (v) => m('table.mails', [
|
||||
m('tr', [
|
||||
m('th[title=starred]', m('i.fas.fa-star')),
|
||||
m('th[title=attachments]', m('i.fas.fa-paperclip')),
|
||||
m('th', 'Subject'),
|
||||
//m('th', 'From'),
|
||||
m('th', 'Date'),
|
||||
view: (v) =>
|
||||
m('table.mails', [
|
||||
m('tr', [
|
||||
m('th[title=starred]', m('i.fas.fa-star')),
|
||||
m('th[title=attachments]', m('i.fas.fa-paperclip')),
|
||||
m('th', 'Subject'),
|
||||
// m('th', 'From'),
|
||||
m('th', 'Date'),
|
||||
]),
|
||||
v.children,
|
||||
]),
|
||||
v.children,
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
@ -133,4 +166,3 @@ module.exports = {
|
||||
RS_MSG_UNREAD_BY_USER,
|
||||
RS_MSG_STAR,
|
||||
};
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ const mail = require('mail/mail_resolver');
|
||||
const files = require('files/files_resolver');
|
||||
const channels = require('channels/channels');
|
||||
const config = require('config/config_resolver');
|
||||
const rs = require('rswebui');
|
||||
|
||||
const navIcon = {
|
||||
home: m('i.fas.fa-home'),
|
||||
@ -19,32 +18,32 @@ const navIcon = {
|
||||
mail: m('i.fas.fa-envelope'),
|
||||
files: m('i.fas.fa-folder-open'),
|
||||
channels: m('i.fas.fa-tv'),
|
||||
config: m('i.fas.fa-cogs')
|
||||
config: m('i.fas.fa-cogs'),
|
||||
};
|
||||
|
||||
const navbar = () => {
|
||||
return {
|
||||
view: vnode =>
|
||||
view: (vnode) =>
|
||||
m(
|
||||
'nav.tab-menu',
|
||||
Object.keys(vnode.attrs.links).map((linkName, i) => {
|
||||
let active = m.route.get().split('/')[1] === linkName
|
||||
const active = m.route.get().split('/')[1] === linkName;
|
||||
return m(
|
||||
m.route.Link,
|
||||
{
|
||||
href: vnode.attrs.links[linkName],
|
||||
class: (active ? 'selected-tab-item' : '') + ' tab-menu-item',
|
||||
class: (active ? 'selected-tab-item' : '') + ' tab-menu-item',
|
||||
},
|
||||
[navIcon[linkName], linkName]
|
||||
);
|
||||
})
|
||||
),
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: vnode =>
|
||||
view: (vnode) =>
|
||||
m('.content', [
|
||||
m(navbar, {
|
||||
links: {
|
||||
@ -55,49 +54,49 @@ const Layout = () => {
|
||||
mail: '/mail/inbox',
|
||||
files: '/files/files',
|
||||
channels: '/channels',
|
||||
config: '/config/network'
|
||||
}
|
||||
config: '/config/network',
|
||||
},
|
||||
}),
|
||||
m('#tab-content', vnode.children)
|
||||
])
|
||||
m('#tab-content', vnode.children),
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
m.route(document.getElementById('main'), '/', {
|
||||
'/': {
|
||||
render: () => m(login)
|
||||
render: () => m(login),
|
||||
},
|
||||
'/home': {
|
||||
render: () => m(Layout, m(home))
|
||||
render: () => m(Layout, m(home)),
|
||||
},
|
||||
'/network': {
|
||||
render: () => m(Layout, m(network))
|
||||
render: () => m(Layout, m(network)),
|
||||
},
|
||||
'/people': {
|
||||
render: () => m(Layout, m(people))
|
||||
render: () => m(Layout, m(people)),
|
||||
},
|
||||
'/chat/:lobby/:subaction': {
|
||||
render: v => m(Layout, m(chat, v.attrs))
|
||||
render: (v) => m(Layout, m(chat, v.attrs)),
|
||||
},
|
||||
'/chat/:lobby': {
|
||||
render: v => m(Layout, m(chat, v.attrs))
|
||||
render: (v) => m(Layout, m(chat, v.attrs)),
|
||||
},
|
||||
'/chat': {
|
||||
render: () => m(Layout, m(chat))
|
||||
render: () => m(Layout, m(chat)),
|
||||
},
|
||||
'/mail/:tab': {
|
||||
render: v => m(Layout, m(mail, v.attrs))
|
||||
render: (v) => m(Layout, m(mail, v.attrs)),
|
||||
},
|
||||
'/mail/:tab/:msgId': {
|
||||
render: v => m(Layout, m(mail, v.attrs))
|
||||
render: (v) => m(Layout, m(mail, v.attrs)),
|
||||
},
|
||||
'/files/:tab': {
|
||||
render: v => m(Layout, m(files, v.attrs))
|
||||
render: (v) => m(Layout, m(files, v.attrs)),
|
||||
},
|
||||
'/channels': {
|
||||
render: () => m(Layout, m(channels))
|
||||
render: () => m(Layout, m(channels)),
|
||||
},
|
||||
'/config/:tab': {
|
||||
render: v => m(Layout, m(config, v.attrs))
|
||||
}
|
||||
render: (v) => m(Layout, m(config, v.attrs)),
|
||||
},
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,25 +1,25 @@
|
||||
const m = require("mithril");
|
||||
const rs = require("rswebui");
|
||||
const widget = require("widgets");
|
||||
const Data = require("network/network_data");
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const widget = require('widgets');
|
||||
const Data = require('network/network_data');
|
||||
|
||||
const ConfirmRemove = () => {
|
||||
return {
|
||||
view: (vnode) => [
|
||||
m("h3", "Remove Friend"),
|
||||
m("hr"),
|
||||
m("p", "Are you sure you want to end connections with this node?"),
|
||||
m('h3', 'Remove Friend'),
|
||||
m('hr'),
|
||||
m('p', 'Are you sure you want to end connections with this node?'),
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
onclick: () => {
|
||||
rs.rsJsonApiRequest("/rsPeers/removeFriend", {
|
||||
rs.rsJsonApiRequest('/rsPeers/removeFriend', {
|
||||
pgpId: vnode.attrs.gpg_id,
|
||||
});
|
||||
m.redraw();
|
||||
},
|
||||
},
|
||||
"Confirm"
|
||||
'Confirm'
|
||||
),
|
||||
],
|
||||
};
|
||||
@ -28,21 +28,21 @@ const ConfirmRemove = () => {
|
||||
const Locations = () => {
|
||||
return {
|
||||
view: (v) => [
|
||||
m("h4", "Locations"),
|
||||
m('h4', 'Locations'),
|
||||
v.attrs.locations.map((loc) =>
|
||||
m(".location", [
|
||||
m("i.fas.fa-user-tag", {style: "margin-top:3px"}),
|
||||
m("span", {style: "margin-top:1px"}, loc.name),
|
||||
m("p", "ID :"),
|
||||
m("p", loc.id),
|
||||
m("p", "Last contacted :"),
|
||||
m("p", new Date(loc.lastSeen * 1000).toDateString()),
|
||||
m("p", "Online :"),
|
||||
m("i.fas", {
|
||||
class: loc.isOnline ? "fa-check-circle" : "fa-times-circle",
|
||||
m('.location', [
|
||||
m('i.fas.fa-user-tag', { style: 'margin-top:3px' }),
|
||||
m('span', { style: 'margin-top:1px' }, loc.name),
|
||||
m('p', 'ID :'),
|
||||
m('p', loc.id),
|
||||
m('p', 'Last contacted :'),
|
||||
m('p', new Date(loc.lastSeen * 1000).toDateString()),
|
||||
m('p', 'Online :'),
|
||||
m('i.fas', {
|
||||
class: loc.isOnline ? 'fa-check-circle' : 'fa-times-circle',
|
||||
}),
|
||||
m(
|
||||
"button.red",
|
||||
'button.red',
|
||||
{
|
||||
onclick: () =>
|
||||
widget.popupMessage(
|
||||
@ -51,7 +51,7 @@ const Locations = () => {
|
||||
})
|
||||
),
|
||||
},
|
||||
"Remove node"
|
||||
'Remove node'
|
||||
),
|
||||
])
|
||||
),
|
||||
@ -65,29 +65,25 @@ const Friend = () => {
|
||||
|
||||
view: (vnode) =>
|
||||
m(
|
||||
".friend",
|
||||
'.friend',
|
||||
{
|
||||
key: vnode.attrs.id,
|
||||
class: Data.gpgDetails[vnode.attrs.id].isSearched ? "" : "hidden",
|
||||
class: Data.gpgDetails[vnode.attrs.id].isSearched ? '' : 'hidden',
|
||||
},
|
||||
[
|
||||
m("i.fas.fa-angle-right", {
|
||||
class: "fa-rotate-" + (vnode.state.isExpanded ? "90" : "0"),
|
||||
style: "margin-top:12px",
|
||||
m('i.fas.fa-angle-right', {
|
||||
class: 'fa-rotate-' + (vnode.state.isExpanded ? '90' : '0'),
|
||||
style: 'margin-top:12px',
|
||||
onclick: () => (vnode.state.isExpanded = !vnode.state.isExpanded),
|
||||
}),
|
||||
m('.brief-info', { class: Data.gpgDetails[vnode.attrs.id].isOnline ? 'online' : '' }, [
|
||||
m('i.fas.fa-2x.fa-user-circle'),
|
||||
m('span', Data.gpgDetails[vnode.attrs.id].name),
|
||||
]),
|
||||
m(
|
||||
".brief-info",
|
||||
{class: Data.gpgDetails[vnode.attrs.id].isOnline ? "online" : ""},
|
||||
[
|
||||
m("i.fas.fa-2x.fa-user-circle"),
|
||||
m("span", Data.gpgDetails[vnode.attrs.id].name),
|
||||
]
|
||||
),
|
||||
m(
|
||||
".details",
|
||||
'.details',
|
||||
{
|
||||
style: "display:" + (vnode.state.isExpanded ? "block" : "none"),
|
||||
style: 'display:' + (vnode.state.isExpanded ? 'block' : 'none'),
|
||||
},
|
||||
[
|
||||
m(Locations, {
|
||||
@ -101,19 +97,17 @@ const Friend = () => {
|
||||
};
|
||||
|
||||
const SearchBar = () => {
|
||||
let searchString = "";
|
||||
let searchString = '';
|
||||
return {
|
||||
view: () =>
|
||||
m("input.searchbar", {
|
||||
type: "text",
|
||||
placeholder: "search",
|
||||
m('input.searchbar', {
|
||||
type: 'text',
|
||||
placeholder: 'search',
|
||||
value: searchString,
|
||||
oninput: (e) => {
|
||||
searchString = e.target.value.toLowerCase();
|
||||
for (let id in Data.gpgDetails) {
|
||||
if (
|
||||
Data.gpgDetails[id].name.toLowerCase().indexOf(searchString) > -1
|
||||
) {
|
||||
for (const id in Data.gpgDetails) {
|
||||
if (Data.gpgDetails[id].name.toLowerCase().indexOf(searchString) > -1) {
|
||||
Data.gpgDetails[id].isSearched = true;
|
||||
} else {
|
||||
Data.gpgDetails[id].isSearched = false;
|
||||
@ -128,24 +122,24 @@ const FriendsList = () => {
|
||||
return {
|
||||
oninit: () => {
|
||||
Data.refreshGpgDetails();
|
||||
//rs.setBackgroundTask(
|
||||
// rs.setBackgroundTask(
|
||||
// Data.refreshGpgDetails,
|
||||
// 10000,
|
||||
// () => m.route.get() === '/network',
|
||||
//)
|
||||
// )
|
||||
},
|
||||
view: () =>
|
||||
m(".widget", [
|
||||
m("h3", "Friend nodes"),
|
||||
m("hr"),
|
||||
m('.widget', [
|
||||
m('h3', 'Friend nodes'),
|
||||
m('hr'),
|
||||
|
||||
Object.entries(Data.gpgDetails)
|
||||
.sort((a, b) => {
|
||||
return a[1].isOnline === b[1].isOnline ? 0 : a[1].isOnline ? -1 : 1;
|
||||
})
|
||||
.map((item) => {
|
||||
id = item[0];
|
||||
return m(Friend, {id});
|
||||
const id = item[0];
|
||||
return m(Friend, { id });
|
||||
}),
|
||||
]),
|
||||
};
|
||||
@ -153,7 +147,7 @@ const FriendsList = () => {
|
||||
|
||||
const Layout = () => {
|
||||
return {
|
||||
view: () => m(".tab-page", [m(SearchBar), m(FriendsList)]),
|
||||
view: () => m('.tab-page', [m(SearchBar), m(FriendsList)]),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,47 +1,36 @@
|
||||
const rs = require('rswebui');
|
||||
|
||||
const Data = {
|
||||
gpgDetails: {},
|
||||
refreshGpgDetails,
|
||||
};
|
||||
|
||||
async function refreshIds() {
|
||||
let sslIds = [];
|
||||
await rs.rsJsonApiRequest(
|
||||
'/rsPeers/getFriendList',
|
||||
{},
|
||||
data => (sslIds = data.sslIds),
|
||||
);
|
||||
await rs.rsJsonApiRequest('/rsPeers/getFriendList', {}, (data) => (sslIds = data.sslIds));
|
||||
return sslIds;
|
||||
}
|
||||
|
||||
async function loadSslDetails() {
|
||||
let sslDetails = [];
|
||||
let sslIds = await refreshIds();
|
||||
const sslDetails = [];
|
||||
const sslIds = await refreshIds();
|
||||
await Promise.all(
|
||||
sslIds.map(sslId =>
|
||||
rs.rsJsonApiRequest('/rsPeers/getPeerDetails', {sslId}, data =>
|
||||
sslDetails.push(data.det),
|
||||
),
|
||||
),
|
||||
sslIds.map((sslId) =>
|
||||
rs.rsJsonApiRequest('/rsPeers/getPeerDetails', { sslId }, (data) => sslDetails.push(data.det))
|
||||
)
|
||||
);
|
||||
return sslDetails;
|
||||
}
|
||||
|
||||
async function refreshGpgDetails() {
|
||||
let details = {};
|
||||
let sslDetails = await loadSslDetails();
|
||||
async function freshGpgDetails() {
|
||||
const details = {};
|
||||
const sslDetails = await loadSslDetails();
|
||||
await Promise.all(
|
||||
sslDetails.map(data => {
|
||||
sslDetails.map((data) => {
|
||||
let isOnline = false;
|
||||
return rs
|
||||
.rsJsonApiRequest(
|
||||
'/rsPeers/isOnline',
|
||||
{sslId: data.id},
|
||||
stat => (isOnline = stat.retval),
|
||||
{ sslId: data.id },
|
||||
(stat) => (isOnline = stat.retval)
|
||||
)
|
||||
.then(() => {
|
||||
loc = {
|
||||
const loc = {
|
||||
name: data.location,
|
||||
id: data.id,
|
||||
lastSeen: data.lastConnect,
|
||||
@ -59,12 +48,18 @@ async function refreshGpgDetails() {
|
||||
} else {
|
||||
details[data.gpg_id].locations.push(loc);
|
||||
}
|
||||
details[data.gpg_id].isOnline =
|
||||
details[data.gpg_id].isOnline || isOnline;
|
||||
details[data.gpg_id].isOnline = details[data.gpg_id].isOnline || isOnline;
|
||||
});
|
||||
}),
|
||||
})
|
||||
);
|
||||
Data.gpgDetails = details;
|
||||
return details;
|
||||
}
|
||||
|
||||
const Data = {
|
||||
gpgDetails: {},
|
||||
refreshGpgDetails() {
|
||||
Data.gpgDetails = freshGpgDetails();
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = Data;
|
||||
|
||||
@ -1,294 +1,290 @@
|
||||
body {
|
||||
background-color: #CCC;
|
||||
color: #666;
|
||||
font-family: sans;
|
||||
margin: 0em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 1.1em;
|
||||
box-sizing: border-box;
|
||||
background-color: #ccc;
|
||||
color: #666;
|
||||
font-family: sans;
|
||||
margin: 0em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 1.1em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-transform: uppercase;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#overlay {
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.paddingbox {
|
||||
padding: 2mm;
|
||||
padding: 2mm;
|
||||
}
|
||||
|
||||
.nav {
|
||||
list-style-type: none;
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
list-style-type: none;
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
.nav li {
|
||||
display: inline;
|
||||
padding: 0.1em;
|
||||
margin-right: 1em;
|
||||
border-width: 0.1em;
|
||||
border-color: blue;
|
||||
border-bottom-style: solid;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
padding: 0.1em;
|
||||
margin-right: 1em;
|
||||
border-width: 0.1em;
|
||||
border-color: blue;
|
||||
border-bottom-style: solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.3em;
|
||||
border-style: none;
|
||||
border-width: 0.1em;
|
||||
border-color: orange;
|
||||
padding: 0.3em;
|
||||
border-style: none;
|
||||
border-width: 0.1em;
|
||||
border-color: orange;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
input[type="button"] {
|
||||
border: solid 1px rgba(0, 0, 0, 0);
|
||||
border-radius: 10px;
|
||||
font-size: 6mm;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
background-color: #f79e3a;
|
||||
width: 26vw;
|
||||
display: inline-grid;
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
box-shadow: 5px 5px 9px #aaa;
|
||||
word-break: break-all;
|
||||
input[type='button'] {
|
||||
border: solid 1px rgba(0, 0, 0, 0);
|
||||
border-radius: 10px;
|
||||
font-size: 6mm;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
background-color: #f79e3a;
|
||||
width: 26vw;
|
||||
display: inline-grid;
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
box-shadow: 5px 5px 9px #aaa;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.btn2,
|
||||
.box,
|
||||
.btnSmall {
|
||||
border: solid 1px rgba(0, 0, 0, 0);
|
||||
border-radius: 10px;
|
||||
font-size: 6mm;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
background-color: #f79e3a;
|
||||
display: inline-grid;
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
box-shadow: 5px 5px 9px #aaa;
|
||||
border: solid 1px rgba(0, 0, 0, 0);
|
||||
border-radius: 10px;
|
||||
font-size: 6mm;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
background-color: #f79e3a;
|
||||
display: inline-grid;
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
box-shadow: 5px 5px 9px #aaa;
|
||||
}
|
||||
|
||||
.btn2,
|
||||
.box
|
||||
{
|
||||
width: 12rem;
|
||||
.box {
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
.btnSmall {
|
||||
width: 7rem;
|
||||
width: 7rem;
|
||||
}
|
||||
|
||||
.menu {
|
||||
border: solid 1px rgba(0, 0, 0, 0);
|
||||
border-radius: 10px;
|
||||
font-size: 0.8em;
|
||||
cursor: pointer;
|
||||
background-color: #f79e3a;
|
||||
display: inline-grid;
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
width: 7rem;
|
||||
border: solid 1px rgba(0, 0, 0, 0);
|
||||
border-radius: 10px;
|
||||
font-size: 0.8em;
|
||||
cursor: pointer;
|
||||
background-color: #f79e3a;
|
||||
display: inline-grid;
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
width: 7rem;
|
||||
}
|
||||
|
||||
span.menu:nth-child(2) {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
width: 6.3rem;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
width: 6.3rem;
|
||||
}
|
||||
|
||||
span.menu:nth-child(16) {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
li .menu {
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
div.btn2:nth-child(9) {
|
||||
border: rgba(0, 0, 0, 0);
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
text-decoration: underline;
|
||||
box-shadow: unset;
|
||||
margin-top: 1em;
|
||||
text-align: left !important;
|
||||
font-size: 1em;
|
||||
border: rgba(0, 0, 0, 0);
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
text-decoration: underline;
|
||||
box-shadow: unset;
|
||||
margin-top: 1em;
|
||||
text-align: left !important;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.btn2:nth-child(9):hover {
|
||||
color: #666;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.btn2:hover {
|
||||
background-color: orangered;
|
||||
color: white;
|
||||
background-color: orangered;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.noBorderTable {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.noBorderTD {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
vertical-align: center;
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
.filelink {
|
||||
color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
border-color: orange;
|
||||
font-size: 10mm;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 1mm;
|
||||
margin-right: 1mm;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
border-color: orange;
|
||||
font-size: 10mm;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 1mm;
|
||||
margin-right: 1mm;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input[type="button"]:hover {
|
||||
background-color: orangered;
|
||||
input[type='button']:hover {
|
||||
background-color: orangered;
|
||||
}
|
||||
|
||||
|
||||
/*chat*/
|
||||
|
||||
textarea#txtNewMsg {
|
||||
font-size: 100%;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
height: 110px;
|
||||
resize: none;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
font-size: 100%;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
height: 110px;
|
||||
resize: none;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input#txtMsgKeyword {
|
||||
/* color: orange;
|
||||
/* color: orange;
|
||||
font-family: monospace;
|
||||
background-color: black;*/
|
||||
border-color: orange;
|
||||
font-size: 100%;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 1mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
border-color: orange;
|
||||
font-size: 100%;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 1mm;
|
||||
margin-bottom: 0mm;
|
||||
margin-right: 1mm;
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.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+ */
|
||||
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+ */
|
||||
-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;
|
||||
-webkit-animation-name: logo_splash;
|
||||
/* Chrome, Safari, Opera */
|
||||
-webkit-animation-duration: 3s;
|
||||
/* Chrome, Safari, Opera */
|
||||
animation-name: logo_splash;
|
||||
animation-duration: 3s;
|
||||
text-align: center;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
/* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-name: logo_splash;
|
||||
/* Chrome, Safari, Opera */
|
||||
-webkit-animation-duration: 3s;
|
||||
/* Chrome, Safari, Opera */
|
||||
animation-name: logo_splash;
|
||||
animation-duration: 3s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/* Chrome, Safari, Opera */
|
||||
|
||||
@-webkit-keyframes logo_splash {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Standard syntax */
|
||||
|
||||
@keyframes logo_splash {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,47 +1,47 @@
|
||||
.identity {
|
||||
color: #444;
|
||||
font-size: 1.1em;
|
||||
margin: 20px;
|
||||
padding: 10px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 20px;
|
||||
color: #444;
|
||||
font-size: 1.1em;
|
||||
margin: 20px;
|
||||
padding: 10px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.identity > h4 {
|
||||
margin: 5px;
|
||||
font-size: 1.3em;
|
||||
margin: 5px;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.identity p {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.identity button {
|
||||
font-size: 0.9em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
img.avatar {
|
||||
float: left;
|
||||
height: 100px;
|
||||
margin-right: 0.3em;
|
||||
float: left;
|
||||
height: 100px;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.identity .details {
|
||||
display: grid;
|
||||
grid-template-columns: 140px auto;
|
||||
grid-row-gap: 5px;
|
||||
justify-content: left;
|
||||
display: grid;
|
||||
grid-template-columns: 140px auto;
|
||||
grid-row-gap: 5px;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.counter{
|
||||
margin-left: 0.5em;
|
||||
.counter {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.counter:before {
|
||||
content: "(";
|
||||
content: '(';
|
||||
}
|
||||
.counter:after {
|
||||
content: ")";
|
||||
content: ')';
|
||||
}
|
||||
|
||||
.chatInit{
|
||||
margin-left: 0.5em;
|
||||
color: green;
|
||||
cursor: pointer;
|
||||
}
|
||||
.chatInit {
|
||||
margin-left: 0.5em;
|
||||
color: green;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -1,34 +1,38 @@
|
||||
let m = require('mithril');
|
||||
let rs = require('rswebui');
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
let OwnIds = require('people/people_ownids');
|
||||
let people_util = require('people/people_util')
|
||||
const peopleOwnIds = require('people/people_ownids');
|
||||
const peopleUtil = require('people/people_util');
|
||||
|
||||
const AllContacts = {
|
||||
oninit: () => {
|
||||
},
|
||||
oninit: () => {},
|
||||
view: () => {
|
||||
let list = people_util.sortUsers(rs.userList.users);
|
||||
const list = peopleUtil.sortUsers(rs.userList.users);
|
||||
return m('.widget', [
|
||||
m('h3', 'Contacts', m('span.counter',list.length)), m('hr'),
|
||||
list.map((id) => m('.id', {
|
||||
key: id.mGroupId,
|
||||
title: 'ID: ' + id.mGroupId,
|
||||
}, id.mGroupName, m('i.chatInit.fa.fa-comment-medical', {
|
||||
title: 'start distant chat with ' + id.mGroupName + ' (' + id.mGroupId + ')',
|
||||
onclick: () => {
|
||||
m.route.set('/chat/:userid/createdistantchat',{userid:id.mGroupId});
|
||||
},
|
||||
})))
|
||||
m('h3', 'Contacts', m('span.counter', list.length)),
|
||||
m('hr'),
|
||||
list.map((id) =>
|
||||
m(
|
||||
'.id',
|
||||
{
|
||||
key: id.mGroupId,
|
||||
title: 'ID: ' + id.mGroupId,
|
||||
},
|
||||
id.mGroupName,
|
||||
m('i.chatInit.fa.fa-comment-medical', {
|
||||
title: 'start distant chat with ' + id.mGroupName + ' (' + id.mGroupId + ')',
|
||||
onclick: () => {
|
||||
m.route.set('/chat/:userid/createdistantchat', { userid: id.mGroupId });
|
||||
},
|
||||
})
|
||||
)
|
||||
),
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
||||
const Layout = {
|
||||
view: vnode => m('.tab-page', [
|
||||
m(OwnIds),
|
||||
m(AllContacts),
|
||||
])
|
||||
view: (vnode) => m('.tab-page', [m(peopleOwnIds), m(AllContacts)]),
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
@ -36,4 +40,3 @@ module.exports = {
|
||||
return m(Layout);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,35 +1,35 @@
|
||||
let m = require("mithril");
|
||||
let rs = require("rswebui");
|
||||
let widget = require("widgets");
|
||||
let people_util = require("people/people_util");
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
const widget = require('widgets');
|
||||
const peopleUtil = require('people/people_util');
|
||||
|
||||
const SignedIdentiy = () => {
|
||||
let passphase = "";
|
||||
let passphase = '';
|
||||
|
||||
return {
|
||||
view: (v) => [
|
||||
m("i.fas.fa-user-edit"),
|
||||
m("h3", "Enter your passpharse"),
|
||||
m("hr"),
|
||||
m('i.fas.fa-user-edit'),
|
||||
m('h3', 'Enter your passpharse'),
|
||||
m('hr'),
|
||||
|
||||
m("input[type=password][placeholder=Passpharse]", {
|
||||
style: "margin-top:50px;width:80%",
|
||||
m('input[type=password][placeholder=Passpharse]', {
|
||||
style: 'margin-top:50px;width:80%',
|
||||
oninput: (e) => {
|
||||
passphase = e.target.value;
|
||||
},
|
||||
}),
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
style: "margin-top:160px;",
|
||||
style: 'margin-top:160px;',
|
||||
onclick: () => {
|
||||
rs.rsJsonApiRequest("/rsIdentity/getOwnSignedIds", {}, (owns) => {
|
||||
rs.rsJsonApiRequest('/rsIdentity/getOwnSignedIds', {}, (owns) => {
|
||||
console.log(owns.ids[0]);
|
||||
console.log(v.attrs.name);
|
||||
|
||||
owns.ids.length > 0
|
||||
? rs.rsJsonApiRequest(
|
||||
"/rsIdentity/createIdentity",
|
||||
'/rsIdentity/createIdentity',
|
||||
{
|
||||
id: owns.ids[0],
|
||||
name: v.attrs.name,
|
||||
@ -38,128 +38,120 @@ const SignedIdentiy = () => {
|
||||
},
|
||||
(data) => {
|
||||
const message = data.retval
|
||||
? "Successfully created identity."
|
||||
: "An error occured while creating identity.";
|
||||
? 'Successfully created identity.'
|
||||
: 'An error occured while creating identity.';
|
||||
console.log(message);
|
||||
widget.popupMessage([
|
||||
m("h3", "Create new Identity"),
|
||||
m("hr"),
|
||||
message,
|
||||
]);
|
||||
widget.popupMessage([m('h3', 'Create new Identity'), m('hr'), message]);
|
||||
}
|
||||
)
|
||||
: widget.popupMessage([
|
||||
m("h3", "Create new Identity"),
|
||||
m("hr"),
|
||||
"An error occured while creating identity.",
|
||||
m('h3', 'Create new Identity'),
|
||||
m('hr'),
|
||||
'An error occured while creating identity.',
|
||||
]);
|
||||
});
|
||||
},
|
||||
},
|
||||
"Enter"
|
||||
'Enter'
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
const CreateIdentity = () => {
|
||||
// TODO: set user avatar
|
||||
let name = "",
|
||||
let name = '',
|
||||
pseudonimous = false;
|
||||
return {
|
||||
view: (v) => [
|
||||
m("i.fas.fa-user-plus"),
|
||||
m("h3", "Create new Identity"),
|
||||
m("hr"),
|
||||
m("input[type=text][placeholder=Name]", {
|
||||
m('i.fas.fa-user-plus'),
|
||||
m('h3', 'Create new Identity'),
|
||||
m('hr'),
|
||||
m('input[type=text][placeholder=Name]', {
|
||||
value: name,
|
||||
oninput: (e) => (name = e.target.value),
|
||||
}),
|
||||
m(
|
||||
"div",
|
||||
'div',
|
||||
{
|
||||
style: "display:inline; margin-left:5px;",
|
||||
style: 'display:inline; margin-left:5px;',
|
||||
},
|
||||
[
|
||||
"Type:",
|
||||
'Type:',
|
||||
m(
|
||||
"select",
|
||||
'select',
|
||||
{
|
||||
value: pseudonimous,
|
||||
style: "border:1px solid black",
|
||||
style: 'border:1px solid black',
|
||||
oninput: (e) => {
|
||||
pseudonimous = e.target.value === "true";
|
||||
pseudonimous = e.target.value === 'true';
|
||||
console.log(pseudonimous);
|
||||
},
|
||||
},
|
||||
[
|
||||
m("option[value=false][selected]", "Linked to your Profile"),
|
||||
m("option[value=true]", "Pseudonymous"),
|
||||
m('option[value=false][selected]', 'Linked to your Profile'),
|
||||
m('option[value=true]', 'Pseudonymous'),
|
||||
]
|
||||
),
|
||||
]
|
||||
),
|
||||
m("br"),
|
||||
m('br'),
|
||||
|
||||
m(
|
||||
"p",
|
||||
"You can have one or more identities. " +
|
||||
"They are used when you chat in lobbies, " +
|
||||
"forums and channel comments. " +
|
||||
"They act as the destination for distant chat and " +
|
||||
"the Retroshare distant mail system."
|
||||
'p',
|
||||
'You can have one or more identities. ' +
|
||||
'They are used when you chat in lobbies, ' +
|
||||
'forums and channel comments. ' +
|
||||
'They act as the destination for distant chat and ' +
|
||||
'the Retroshare distant mail system.'
|
||||
),
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
onclick: () => {
|
||||
!pseudonimous
|
||||
? widget.popupMessage(m(SignedIdentiy, {name: name}))
|
||||
? widget.popupMessage(m(SignedIdentiy, { name }))
|
||||
: rs.rsJsonApiRequest(
|
||||
"/rsIdentity/createIdentity",
|
||||
'/rsIdentity/createIdentity',
|
||||
{
|
||||
name,
|
||||
pseudonimous,
|
||||
},
|
||||
(data) => {
|
||||
const message = data.retval
|
||||
? "Successfully created identity."
|
||||
: "An error occured while creating identity.";
|
||||
widget.popupMessage([
|
||||
m("h3", "Create new Identity"),
|
||||
m("hr"),
|
||||
message,
|
||||
]);
|
||||
? 'Successfully created identity.'
|
||||
: 'An error occured while creating identity.';
|
||||
widget.popupMessage([m('h3', 'Create new Identity'), m('hr'), message]);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
"Create"
|
||||
'Create'
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const SignedEditIdentity = () => {
|
||||
let passphase = "";
|
||||
let passphase = '';
|
||||
return {
|
||||
view: (v) => [
|
||||
m("i.fas.fa-user-edit"),
|
||||
m("h3", "Enter your passpharse"),
|
||||
m("hr"),
|
||||
m('i.fas.fa-user-edit'),
|
||||
m('h3', 'Enter your passpharse'),
|
||||
m('hr'),
|
||||
|
||||
m("input[type=password][placeholder=Passpharse]", {
|
||||
style: "margin-top:50px;width:80%",
|
||||
m('input[type=password][placeholder=Passpharse]', {
|
||||
style: 'margin-top:50px;width:80%',
|
||||
oninput: (e) => {
|
||||
passphase = e.target.value;
|
||||
},
|
||||
}),
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
style: "margin-top:160px;",
|
||||
style: 'margin-top:160px;',
|
||||
onclick: () =>
|
||||
rs.rsJsonApiRequest(
|
||||
"/rsIdentity/updateIdentity",
|
||||
'/rsIdentity/updateIdentity',
|
||||
{
|
||||
id: v.attrs.details.mId,
|
||||
name: v.attrs.name,
|
||||
@ -168,69 +160,61 @@ const SignedEditIdentity = () => {
|
||||
},
|
||||
(data) => {
|
||||
const message = data.retval
|
||||
? "Successfully created identity."
|
||||
: "An error occured while creating identity.";
|
||||
widget.popupMessage([
|
||||
m("h3", "Create new Identity"),
|
||||
m("hr"),
|
||||
message,
|
||||
]);
|
||||
? 'Successfully created identity.'
|
||||
: 'An error occured while creating identity.';
|
||||
widget.popupMessage([m('h3', 'Create new Identity'), m('hr'), message]);
|
||||
}
|
||||
),
|
||||
},
|
||||
"Enter"
|
||||
'Enter'
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const EditIdentity = () => {
|
||||
let name = "";
|
||||
let name = '';
|
||||
return {
|
||||
view: (v) => [
|
||||
m("i.fas.fa-user-edit"),
|
||||
m("h3", "Edit Identity"),
|
||||
m("hr"),
|
||||
m("input[type=text][placeholder=Name]", {
|
||||
m('i.fas.fa-user-edit'),
|
||||
m('h3', 'Edit Identity'),
|
||||
m('hr'),
|
||||
m('input[type=text][placeholder=Name]', {
|
||||
value: name,
|
||||
oninput: (e) => {
|
||||
name = e.target.value;
|
||||
},
|
||||
}),
|
||||
m("canvas"),
|
||||
m('canvas'),
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
onclick: () => {
|
||||
!people_util.checksudo(v.attrs.details.mPgpId)
|
||||
!peopleUtil.checksudo(v.attrs.details.mPgpId)
|
||||
? widget.popupMessage([
|
||||
m(SignedEditIdentity, {
|
||||
name: name,
|
||||
name,
|
||||
details: v.attrs.details,
|
||||
}),
|
||||
])
|
||||
: rs.rsJsonApiRequest(
|
||||
"/rsIdentity/updateIdentity",
|
||||
'/rsIdentity/updateIdentity',
|
||||
{
|
||||
id: v.attrs.details.mId,
|
||||
name: name,
|
||||
//avatar: v.attrs.details.mAvatar.mData.base64,
|
||||
name,
|
||||
// avatar: v.attrs.details.mAvatar.mData.base64,
|
||||
pseudonimous: true,
|
||||
},
|
||||
(data) => {
|
||||
const message = data.retval
|
||||
? "Successfully Updated identity."
|
||||
: "An error occured while updating identity.";
|
||||
widget.popupMessage([
|
||||
m("h3", "Update Identity"),
|
||||
m("hr"),
|
||||
message,
|
||||
]);
|
||||
? 'Successfully Updated identity.'
|
||||
: 'An error occured while updating identity.';
|
||||
widget.popupMessage([m('h3', 'Update Identity'), m('hr'), message]);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
"Save"
|
||||
'Save'
|
||||
),
|
||||
],
|
||||
};
|
||||
@ -239,33 +223,30 @@ const EditIdentity = () => {
|
||||
const DeleteIdentity = () => {
|
||||
return {
|
||||
view: (v) => [
|
||||
m("i.fas.fa-user-times"),
|
||||
m("h3", "Delete Identity: " + v.attrs.name),
|
||||
m("hr"),
|
||||
m('i.fas.fa-user-times'),
|
||||
m('h3', 'Delete Identity: ' + v.attrs.name),
|
||||
m('hr'),
|
||||
m('p', 'Are you sure you want to delete this Identity? It cannot be restore'),
|
||||
m(
|
||||
"p",
|
||||
"Are you sure you want to delete this Identity? It cannot be restore"
|
||||
),
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
onclick: () =>
|
||||
rs.rsJsonApiRequest(
|
||||
"/rsIdentity/deleteIdentity",
|
||||
'/rsIdentity/deleteIdentity',
|
||||
{
|
||||
id: v.attrs.id,
|
||||
},
|
||||
() => {
|
||||
widget.popupMessage([
|
||||
m("i.fas.fa-user-edit"),
|
||||
m("h3", "Delete Identity: " + v.attrs.name),
|
||||
m("hr"),
|
||||
m("p", "Identity Deleted successfuly."),
|
||||
m('i.fas.fa-user-edit'),
|
||||
m('h3', 'Delete Identity: ' + v.attrs.name),
|
||||
m('hr'),
|
||||
m('p', 'Identity Deleted successfuly.'),
|
||||
]);
|
||||
}
|
||||
),
|
||||
},
|
||||
"Confirm"
|
||||
'Confirm'
|
||||
),
|
||||
],
|
||||
};
|
||||
@ -281,7 +262,7 @@ const Identity = () => {
|
||||
return {
|
||||
oninit: (v) =>
|
||||
rs.rsJsonApiRequest(
|
||||
"/rsIdentity/getIdDetails",
|
||||
'/rsIdentity/getIdDetails',
|
||||
{
|
||||
id: v.attrs.id,
|
||||
},
|
||||
@ -289,56 +270,54 @@ const Identity = () => {
|
||||
details = data.details;
|
||||
// Creating URI during fetch because `details` is uninitialized
|
||||
// during view run, due to request being async.
|
||||
avatarURI = people_util.createAvatarURI(data.details.mAvatar);
|
||||
avatarURI = peopleUtil.createAvatarURI(data.details.mAvatar);
|
||||
}
|
||||
),
|
||||
view: (v) =>
|
||||
m(
|
||||
".identity",
|
||||
'.identity',
|
||||
{
|
||||
key: details.mId,
|
||||
},
|
||||
[
|
||||
m("h4", details.mNickname),
|
||||
m('h4', details.mNickname),
|
||||
m(avatarURI),
|
||||
m(".details", [
|
||||
m("p", "ID:"),
|
||||
m("p", details.mId),
|
||||
m("p", "Type:"),
|
||||
m("p", details.mFlags === 14 ? "Signed ID" : "Anonymous ID"),
|
||||
m("p", "Owner node ID:"),
|
||||
m("p", details.mPgpId),
|
||||
m("p", "Created on:"),
|
||||
m('.details', [
|
||||
m('p', 'ID:'),
|
||||
m('p', details.mId),
|
||||
m('p', 'Type:'),
|
||||
m('p', details.mFlags === 14 ? 'Signed ID' : 'Anonymous ID'),
|
||||
m('p', 'Owner node ID:'),
|
||||
m('p', details.mPgpId),
|
||||
m('p', 'Created on:'),
|
||||
m(
|
||||
"p",
|
||||
typeof details.mPublishTS === "object"
|
||||
'p',
|
||||
typeof details.mPublishTS === 'object'
|
||||
? new Date(details.mPublishTS.xint64 * 1000).toLocaleString()
|
||||
: "undefiend"
|
||||
: 'undefiend'
|
||||
),
|
||||
m("p", "Last used:"),
|
||||
m('p', 'Last used:'),
|
||||
m(
|
||||
"p",
|
||||
typeof details.mLastUsageTS === "object"
|
||||
? new Date(
|
||||
details.mLastUsageTS.xint64 * 1000
|
||||
).toLocaleDateString()
|
||||
: "undefiend"
|
||||
'p',
|
||||
typeof details.mLastUsageTS === 'object'
|
||||
? new Date(details.mLastUsageTS.xint64 * 1000).toLocaleDateString()
|
||||
: 'undefiend'
|
||||
),
|
||||
]),
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
onclick: () =>
|
||||
widget.popupMessage(
|
||||
m(EditIdentity, {
|
||||
details: details,
|
||||
details,
|
||||
})
|
||||
),
|
||||
},
|
||||
"Edit"
|
||||
'Edit'
|
||||
),
|
||||
m(
|
||||
"button.red",
|
||||
'button.red',
|
||||
{
|
||||
onclick: () =>
|
||||
widget.popupMessage(
|
||||
@ -348,7 +327,7 @@ const Identity = () => {
|
||||
})
|
||||
),
|
||||
},
|
||||
"Delete"
|
||||
'Delete'
|
||||
),
|
||||
]
|
||||
),
|
||||
@ -358,18 +337,18 @@ const Identity = () => {
|
||||
const Layout = () => {
|
||||
let ownIds = [];
|
||||
return {
|
||||
oninit: () => people_util.ownIds((data) => (ownIds = data)),
|
||||
oninit: () => peopleUtil.ownIds((data) => (ownIds = data)),
|
||||
view: () =>
|
||||
m(".widget", [
|
||||
m("h3", "Own Identities", m("span.counter", ownIds.length)),
|
||||
m("hr"),
|
||||
m('.widget', [
|
||||
m('h3', 'Own Identities', m('span.counter', ownIds.length)),
|
||||
m('hr'),
|
||||
|
||||
m(
|
||||
"button",
|
||||
'button',
|
||||
{
|
||||
onclick: () => widget.popupMessage(m(CreateIdentity)),
|
||||
},
|
||||
"New Identity"
|
||||
'New Identity'
|
||||
),
|
||||
ownIds.map((id) =>
|
||||
m(Identity, {
|
||||
|
||||
@ -1,34 +1,13 @@
|
||||
let m = require("mithril");
|
||||
let rs = require("rswebui");
|
||||
|
||||
function getInfo(id) {
|
||||
let name = "";
|
||||
let imageURI = "";
|
||||
rs.rsJsonApiRequest(
|
||||
"/rsIdentity/getIdentitiesInfo",
|
||||
{
|
||||
id,
|
||||
},
|
||||
function (data) {
|
||||
console.log("data: ", data.idsInfo);
|
||||
//name = data.details.mNickname
|
||||
//imageURI = data.details.mAvatar.mData;
|
||||
}
|
||||
);
|
||||
//return m('.profile-detail', [
|
||||
//m('img[src=data:image/png;base64,' + imageURI + ']'),
|
||||
// name//,
|
||||
//]);
|
||||
return "name";
|
||||
}
|
||||
const m = require('mithril');
|
||||
const rs = require('rswebui');
|
||||
|
||||
function checksudo(id) {
|
||||
return id === "0000000000000000";
|
||||
return id === '0000000000000000';
|
||||
}
|
||||
|
||||
function sortUsers(list) {
|
||||
if (list !== undefined) {
|
||||
let result = [...list];
|
||||
const result = [...list];
|
||||
result.sort((a, b) => a.mGroupName.localeCompare(b.mGroupName));
|
||||
return result;
|
||||
}
|
||||
@ -37,21 +16,19 @@ function sortUsers(list) {
|
||||
|
||||
function sortIds(list) {
|
||||
if (list !== undefined) {
|
||||
let result = [...list];
|
||||
result.sort((a, b) =>
|
||||
rs.userList.username(a).localeCompare(rs.userList.username(b))
|
||||
);
|
||||
const result = [...list];
|
||||
result.sort((a, b) => rs.userList.username(a).localeCompare(rs.userList.username(b)));
|
||||
return result;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
function ownIds(consumer = (list) => {}, onlySigned = false) {
|
||||
rs.rsJsonApiRequest("/rsIdentity/getOwnSignedIds", {}, (owns) => {
|
||||
rs.rsJsonApiRequest('/rsIdentity/getOwnSignedIds', {}, (owns) => {
|
||||
if (onlySigned) {
|
||||
consumer(sortIds(owns.ids));
|
||||
} else {
|
||||
rs.rsJsonApiRequest("/rsIdentity/getOwnPseudonimousIds", {}, (pseudo) =>
|
||||
rs.rsJsonApiRequest('/rsIdentity/getOwnPseudonimousIds', {}, (pseudo) =>
|
||||
consumer(sortIds(pseudo.ids.concat(owns.ids)))
|
||||
);
|
||||
}
|
||||
@ -59,23 +36,22 @@ function ownIds(consumer = (list) => {}, onlySigned = false) {
|
||||
}
|
||||
|
||||
function createAvatarURI(avatar) {
|
||||
return avatar.mData.base64 === ""
|
||||
return avatar.mData.base64 === ''
|
||||
? {
|
||||
view: () => [],
|
||||
}
|
||||
: {
|
||||
view: () =>
|
||||
m("img.avatar", {
|
||||
src: "data:image/png;base64," + avatar.mData.base64,
|
||||
m('img.avatar', {
|
||||
src: 'data:image/png;base64,' + avatar.mData.base64,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getInfo,
|
||||
sortUsers,
|
||||
sortIds,
|
||||
ownIds,
|
||||
createAvatarURI,
|
||||
checksudo
|
||||
checksudo,
|
||||
};
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
const m = require('mithril');
|
||||
|
||||
const API_URL = 'http://127.0.0.1:9092';
|
||||
let loginKey = {
|
||||
const loginKey = {
|
||||
username: '',
|
||||
passwd: '',
|
||||
isVerified: false,
|
||||
url: API_URL
|
||||
url: API_URL,
|
||||
};
|
||||
|
||||
// Make this as object property?
|
||||
@ -28,8 +28,7 @@ function rsJsonApiRequest(
|
||||
) {
|
||||
headers['Accept'] = 'application/json';
|
||||
if (loginKey.isVerified) {
|
||||
headers['Authorization'] =
|
||||
'Basic ' + btoa(loginKey.username + ':' + loginKey.passwd);
|
||||
headers['Authorization'] = 'Basic ' + btoa(loginKey.username + ':' + loginKey.passwd);
|
||||
}
|
||||
// NOTE: After upgrading to mithrilv2, options.extract is no longer required
|
||||
// since the status will become part of return value and then
|
||||
@ -39,21 +38,21 @@ function rsJsonApiRequest(
|
||||
method: 'POST',
|
||||
url: loginKey.url + path,
|
||||
async,
|
||||
extract: xhr => {
|
||||
extract: (xhr) => {
|
||||
// Empty string is not valid json and fails on parse
|
||||
let response = xhr.responseText || '""';
|
||||
const response = xhr.responseText || '""';
|
||||
return {
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText,
|
||||
body: handleDeserialize(response)
|
||||
body: handleDeserialize(response),
|
||||
};
|
||||
},
|
||||
serialize: handleSerialize,
|
||||
headers: headers,
|
||||
headers,
|
||||
body: data,
|
||||
config: config
|
||||
config,
|
||||
})
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
if (result.status === 200) {
|
||||
callback(result.body, true);
|
||||
} else {
|
||||
@ -63,14 +62,9 @@ function rsJsonApiRequest(
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.catch(function(e) {
|
||||
.catch(function (e) {
|
||||
callback(e, false);
|
||||
console.error(
|
||||
'Error: While sending request for path:',
|
||||
path,
|
||||
'\ninfo:',
|
||||
e
|
||||
);
|
||||
console.error('Error: While sending request for path:', path, '\ninfo:', e);
|
||||
m.route.set('/');
|
||||
});
|
||||
}
|
||||
@ -91,81 +85,88 @@ function setBackgroundTask(task, interval, taskInScope) {
|
||||
}
|
||||
|
||||
function computeIfMissing(map, key, missing = () => ({})) {
|
||||
if (!map.hasOwnProperty(key)) {
|
||||
map[key] = missing();
|
||||
}
|
||||
return map[key];
|
||||
if (!Object.prototype.hasOwnProperty.call(map, key)) {
|
||||
map[key] = missing();
|
||||
}
|
||||
return map[key];
|
||||
}
|
||||
|
||||
function deeperIfExist(map, key, action) {
|
||||
if (map.hasOwnProperty(key)) {
|
||||
action(map[key]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(map, key)) {
|
||||
action(map[key]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const eventQueue = {
|
||||
events: {
|
||||
15: { // Chat-Messages
|
||||
types: {
|
||||
// #define RS_CHAT_TYPE_PUBLIC 1
|
||||
// #define RS_CHAT_TYPE_PRIVATE 2
|
||||
2: chat_id => chat_id.distant_chat_id, // distant chat (initiate? -> todo accept)
|
||||
// #define RS_CHAT_TYPE_LOBBY 3
|
||||
3: chat_id => chat_id.lobby_id.xstr64, // lobby_id
|
||||
// #define RS_CHAT_TYPE_DISTANT 4
|
||||
},
|
||||
messages: {},
|
||||
chatMessages: (chat_id, owner, action) => {
|
||||
if (!deeperIfExist(
|
||||
owner.types,
|
||||
chat_id.type,
|
||||
keyfn => action(computeIfMissing(computeIfMissing(owner.messages,chat_id.type), keyfn(chat_id),()=>[]))
|
||||
)) {
|
||||
console.info('unknown chat event', chat_id);
|
||||
};
|
||||
},
|
||||
handler: (event,owner) => owner.chatMessages(event.mChatMessage.chat_id, owner, r => {
|
||||
console.info('adding chat' ,r , event.mChatMessage);
|
||||
r.push(event.mChatMessage);
|
||||
owner.notify(event.mChatMessage);
|
||||
}),
|
||||
notify: () => {},
|
||||
},
|
||||
8: { // Circles (ignore in the meantime)
|
||||
handler: (event,owner) => {},
|
||||
events: {
|
||||
15: {
|
||||
// Chat-Messages
|
||||
types: {
|
||||
// #define RS_CHAT_TYPE_PUBLIC 1
|
||||
// #define RS_CHAT_TYPE_PRIVATE 2
|
||||
2: (chatId) => chatId.distant_chat_id, // distant chat (initiate? -> todo accept)
|
||||
// #define RS_CHAT_TYPE_LOBBY 3
|
||||
3: (chatId) => chatId.lobby_id.xstr64, // lobby_id
|
||||
// #define RS_CHAT_TYPE_DISTANT 4
|
||||
},
|
||||
messages: {},
|
||||
chatMessages: (chatId, owner, action) => {
|
||||
if (
|
||||
!deeperIfExist(owner.types, chatId.type, (keyfn) =>
|
||||
action(
|
||||
computeIfMissing(
|
||||
computeIfMissing(owner.messages, chatId.type),
|
||||
keyfn(chatId),
|
||||
() => []
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
console.info('unknown chat event', chatId);
|
||||
}
|
||||
},
|
||||
handler: (event, owner) =>
|
||||
owner.chatMessages(event.mChatMessage.chat_id, owner, (r) => {
|
||||
console.info('adding chat', r, event.mChatMessage);
|
||||
r.push(event.mChatMessage);
|
||||
owner.notify(event.mChatMessage);
|
||||
}),
|
||||
notify: () => {},
|
||||
},
|
||||
handler: event => {
|
||||
if (!deeperIfExist(eventQueue.events, event.mType, owner => owner.handler(event, owner))) {
|
||||
console.info('unhandled event', event);
|
||||
};
|
||||
8: {
|
||||
// Circles (ignore in the meantime)
|
||||
handler: (event, owner) => {},
|
||||
},
|
||||
}
|
||||
},
|
||||
handler: (event) => {
|
||||
if (!deeperIfExist(eventQueue.events, event.mType, (owner) => owner.handler(event, owner))) {
|
||||
console.info('unhandled event', event);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const userList = {
|
||||
users: [],
|
||||
userMap: {},
|
||||
loadUsers: () => {
|
||||
rsJsonApiRequest('/rsIdentity/getIdentitiesSummaries', {},
|
||||
list=> {
|
||||
if (list!== undefined) {
|
||||
console.info('loading ' + list.ids.length + ' users ...');
|
||||
userList.users=list.ids;
|
||||
userList.userMap = list.ids.reduce((a,c) => {
|
||||
a[c.mGroupId] = c.mGroupName;
|
||||
return a;
|
||||
}, {});
|
||||
}
|
||||
rsJsonApiRequest('/rsIdentity/getIdentitiesSummaries', {}, (list) => {
|
||||
if (list !== undefined) {
|
||||
console.info('loading ' + list.ids.length + ' users ...');
|
||||
userList.users = list.ids;
|
||||
userList.userMap = list.ids.reduce((a, c) => {
|
||||
a[c.mGroupId] = c.mGroupName;
|
||||
return a;
|
||||
}, {});
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
username: id => {
|
||||
return userList.userMap[id]||id;
|
||||
username: (id) => {
|
||||
return userList.userMap[id] || id;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
path,
|
||||
@ -177,52 +178,77 @@ const userList = {
|
||||
handleSerialize = JSON.stringify
|
||||
config
|
||||
*/
|
||||
function startEventQueue(info, loginHeader = {}, displayAuthError = () => {}, displayErrorMessage = () => {}, successful = () => {}){
|
||||
return rsJsonApiRequest('/rsEvents/registerEventsHandler', {},
|
||||
function startEventQueue(
|
||||
info,
|
||||
loginHeader = {},
|
||||
displayAuthError = () => {},
|
||||
displayErrorMessage = () => {},
|
||||
successful = () => {}
|
||||
) {
|
||||
return rsJsonApiRequest(
|
||||
'/rsEvents/registerEventsHandler',
|
||||
{},
|
||||
(data, success) => {
|
||||
if (success) {
|
||||
// unused
|
||||
} else if (data.status == 401) {
|
||||
} else if (data.status === 401) {
|
||||
displayAuthError('Incorrect login/password.');
|
||||
} else if (data.status == 0) {
|
||||
displayErrorMessage(['Retroshare-jsonapi not available.',m('br'),'Please fix host and/or port.']);
|
||||
} else if (data.status === 0) {
|
||||
displayErrorMessage([
|
||||
'Retroshare-jsonapi not available.',
|
||||
m('br'),
|
||||
'Please fix host and/or port.',
|
||||
]);
|
||||
} else {
|
||||
displayErrorMessage('Login failed: HTTP ' + data.status + ' ' + data.statusText);
|
||||
}
|
||||
},
|
||||
true, loginHeader, JSON.parse, JSON.stringify,
|
||||
true,
|
||||
loginHeader,
|
||||
JSON.parse,
|
||||
JSON.stringify,
|
||||
(xhr, args, url) => {
|
||||
let lastIndex = 0;
|
||||
xhr.onprogress = ev => {
|
||||
let currIndex = xhr.responseText.length;
|
||||
xhr.onprogress = (ev) => {
|
||||
const currIndex = xhr.responseText.length;
|
||||
if (currIndex > lastIndex) {
|
||||
let parts = xhr.responseText.substring(lastIndex,currIndex);
|
||||
lastIndex=currIndex;
|
||||
for(data of parts.trim().split('\n\n').filter(e=> e.startsWith('data: {')).map(e=> e.substr(6)).map(JSON.parse)){
|
||||
if (data.hasOwnProperty('retval')) {
|
||||
console.info(info + ' [' + data.retval.errorCategory + '] ' + data.retval.errorMessage);
|
||||
const parts = xhr.responseText.substring(lastIndex, currIndex);
|
||||
lastIndex = currIndex;
|
||||
for (const data of parts
|
||||
.trim()
|
||||
.split('\n\n')
|
||||
.filter((e) => e.startsWith('data: {'))
|
||||
.map((e) => e.substr(6))
|
||||
.map(JSON.parse)) {
|
||||
if (Object.prototype.hasOwnProperty.call(data, 'retval')) {
|
||||
console.info(
|
||||
info + ' [' + data.retval.errorCategory + '] ' + data.retval.errorMessage
|
||||
);
|
||||
if (data.retval.errorNumber === 0) {
|
||||
successful();
|
||||
} else {
|
||||
displayErrorMessage(info + ' failed: [' + data.retval.errorCategory + '] ' + data.retval.errorMessage);
|
||||
displayErrorMessage(
|
||||
info + ' failed: [' + data.retval.errorCategory + '] ' + data.retval.errorMessage
|
||||
);
|
||||
}
|
||||
} else if(data.hasOwnProperty('event')) {
|
||||
} else if (Object.prototype.hasOwnProperty.call(data, 'event')) {
|
||||
data.event.queueSize = currIndex;
|
||||
eventQueue.handler(data.event);
|
||||
}
|
||||
}
|
||||
if (currIndex > 1e5) { // max 100 kB eventQueue
|
||||
if (currIndex > 1e5) {
|
||||
// max 100 kB eventQueue
|
||||
startEventQueue('restart queue');
|
||||
xhr.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return xhr;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function logon(loginHeader, displayAuthError, displayErrorMessage, successful){
|
||||
function logon(loginHeader, displayAuthError, displayErrorMessage, successful) {
|
||||
startEventQueue('login', loginHeader, displayAuthError, displayErrorMessage, () => {
|
||||
successful();
|
||||
userList.loadUsers();
|
||||
|
||||
@ -24,9 +24,9 @@ General site-wide rules
|
||||
animation: fadein 0.5s;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
input[type='text'],
|
||||
input[type='password'],
|
||||
input[type='number'],
|
||||
textarea {
|
||||
box-sizing: border-box;
|
||||
background: white;
|
||||
@ -130,7 +130,7 @@ hr {
|
||||
grid-row-gap: 16px;
|
||||
justify-content: start;
|
||||
}
|
||||
.grid-2col input[type="checkbox"] {
|
||||
.grid-2col input[type='checkbox'] {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,23 @@
|
||||
let m = require('mithril');
|
||||
|
||||
const m = require('mithril');
|
||||
|
||||
const Sidebar = () => {
|
||||
let active = 0;
|
||||
return {
|
||||
view: (v) => m('.sidebar',
|
||||
v.attrs.tabs.map((panelName, index) => m(m.route.Link, {
|
||||
class: index === active? 'selected-sidebar-link': '',
|
||||
onclick: () => (active = index),
|
||||
href: v.attrs.baseRoute + panelName
|
||||
}, panelName)),
|
||||
),
|
||||
view: (v) =>
|
||||
m(
|
||||
'.sidebar',
|
||||
v.attrs.tabs.map((panelName, index) =>
|
||||
m(
|
||||
m.route.Link,
|
||||
{
|
||||
class: index === active ? 'selected-sidebar-link' : '',
|
||||
onclick: () => (active = index),
|
||||
href: v.attrs.baseRoute + panelName,
|
||||
},
|
||||
panelName
|
||||
)
|
||||
)
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@ -20,13 +27,19 @@ const Sidebar = () => {
|
||||
function popupMessage(message) {
|
||||
const container = document.getElementById('modal-container');
|
||||
container.style.display = 'block';
|
||||
m.render(container,
|
||||
m.render(
|
||||
container,
|
||||
m('.modal-content', [
|
||||
m('button.red', {
|
||||
onclick: () => container.style.display = 'none'
|
||||
}, m('i.fas.fa-times')),
|
||||
m(
|
||||
'button.red',
|
||||
{
|
||||
onclick: () => (container.style.display = 'none'),
|
||||
},
|
||||
m('i.fas.fa-times')
|
||||
),
|
||||
message,
|
||||
]));
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user