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:
zeners 2021-04-20 15:00:39 +02:00 committed by GitHub
parent 6bbdb04824
commit f09d6b0ca9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 4502 additions and 3970 deletions

View File

@ -17,10 +17,6 @@
},
"ignorePatterns": ["mithril.js", "assets/*", "make-src/*"],
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"

View File

@ -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;
}

View File

@ -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);
},
};

View File

@ -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;
}

View File

@ -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);
}
}
},
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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]));
},
};

View File

@ -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);
},
};

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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,
};

View File

@ -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]));
},
};

View File

@ -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);
},
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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],
})
);
},
};

View File

@ -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;

View File

@ -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,
};

View File

@ -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

View File

@ -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)]),
};
};

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
},
};

View File

@ -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, {

View File

@ -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,
};

View File

@ -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();

View File

@ -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;
}

View File

@ -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 = {