Merge pull request #4 from rottencandy/async-network

Better display for friend nodes in network tab
This commit is contained in:
csoler 2019-11-09 16:11:23 +01:00 committed by GitHub
commit 6fc29f565d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 192 additions and 116 deletions

View File

@ -20,6 +20,12 @@
.friend button {
font-size: 0.9em;
}
.friend.hidden {
display: none;
}
.friend .brief-info.online {
color: green;
}
.friend .location {
margin: 5px;

View File

@ -1,151 +1,151 @@
let m = require('mithril');
let rs = require('rswebui');
let widget = require('widgets');
const m = require('mithril');
const rs = require('rswebui');
const widget = require('widgets');
const Data = require('network_data');
const ConfirmRemove = () => {
return {
view: (vnode) => [m('h3', 'Remove Friend'),
view: vnode => [
m('h3', 'Remove Friend'),
m('hr'),
m('p', 'Are you sure you want to end connections with this node?'),
m('button', {
onclick: () => {
rs.rsJsonApiRequest('/rsPeers/removeFriend', {
pgpId: vnode.attrs.gpg
});
m.redraw();
}
}, 'Confirm'),
m(
'button',
{
onclick: () => {
rs.rsJsonApiRequest('/rsPeers/removeFriend', {
pgpId: vnode.attrs.gpg_id,
});
m.redraw();
},
},
'Confirm',
),
],
};
};
const Node = () => {
const Locations = () => {
return {
isOnline: false,
isExpanded: false,
oninit(v) {
v.state.isOnline = false;
// check if any one location is online
v.attrs.data.map(node => rs.rsJsonApiRequest(
'/rsPeers/isOnline', {
sslId: node.id
}, (data) => data.retval ? v.state.isOnline = true : undefined));
},
view: (vnode) => m('.friend', {
key: vnode.attrs.data[0].gpg_id,
style: "display:" + (vnode.attrs.isSearched ? "block" : "none"),
}, [
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (vnode.state.isExpanded ? '90' : '0'),
onclick: () => vnode.state.isExpanded = !vnode.state.isExpanded,
}),
m('i.fas.fa-2x.fa-user-circle'),
m('span', vnode.attrs.data[0].name),
m('i.fas', {
class: vnode.state.isOnline ? 'fa-check-circle' : 'fa-times-circle'
}),
m('.details', {
style: "display:" + (vnode.state.isExpanded ? "block" : "none"),
}, [
m('.grid-2col', [
m('p', 'Last contacted :'),
m('p', new Date(vnode.attrs.data[0].lastConnect * 1000).toDateString()),
m('p', 'Online :'),
m('i.fas', {
class: vnode.state.isOnline ? 'fa-check-circle' : 'fa-times-circle'
}),
]),
m('h4', 'Locations'),
vnode.attrs.data.map((loc) => m('.location', [
m('i.fas.fa-user-tag'), m('span', loc.location),
view: v => [
m('h4', 'Locations'),
v.attrs.locations.map(loc =>
m('.location', [
m('i.fas.fa-user-tag'),
m('span', 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: vnode.state.isOnline ? 'fa-check-circle' : 'fa-times-circle'
class: loc.isOnline ? 'fa-check-circle' : 'fa-times-circle',
}),
m('button.red', {
onclick: () => widget.popupMessage(m(ConfirmRemove, {
gpg: loc.gpg_id,
}))
}, 'Remove node'),
])),
])
]),
m(
'button.red',
{
onclick: () =>
widget.popupMessage(
m(ConfirmRemove, {
gpg: loc.gpg_id,
}),
),
},
'Remove node',
),
]),
),
],
};
};
let FriendNodes = {};
const Friend = () => {
return {
isExpanded: false,
view: vnode =>
m(
'.friend',
{
key: vnode.attrs.id,
class: Data.gpgDetails[vnode.attrs.id].isSearched ? '' : 'hidden',
},
[
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (vnode.state.isExpanded ? '90' : '0'),
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(
'.details',
{
style: 'display:' + (vnode.state.isExpanded ? 'block' : 'none'),
},
[
m(Locations, {
locations: Data.gpgDetails[vnode.attrs.id].locations,
}),
],
),
],
),
};
};
let searchString = '';
const SearchBar = () => {
let searchString = '';
return {
view: () => m('input[type=text][placeholder=search].searchbar', {
value: searchString,
oninput: (e) => {
searchString = e.target.value.toLowerCase();
for(let id in FriendNodes) {
if(FriendNodes[id].locations[0].name.toLowerCase().indexOf(
searchString) > -1) {
FriendNodes[id].isSearched = true;
} else {
FriendNodes[id].isSearched = false;
view: () =>
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
) {
Data.gpgDetails[id].isSearched = true;
} else {
Data.gpgDetails[id].isSearched = false;
}
}
}
},
})
},
}),
};
};
const Friends = () => {
const FriendsList = () => {
return {
oninit: () => {
FriendNodes = {};
rs.rsJsonApiRequest(
'/rsPeers/getFriendList', {},
(friendListIds) => {
friendListIds.sslIds.map(
(sslId) => rs.rsJsonApiRequest(
'/rsPeers/getPeerDetails', {
sslId
},
(details) => {
// Store nodes obj with gpg id as key
// single node can have multiple identities
if(FriendNodes[details.det.gpg_id] === undefined)
FriendNodes[details.det.gpg_id] = {
locations: [details.det],
isSearched: true
}
else
FriendNodes[details.det.gpg_id].locations.push(
details.det);
})
);
});
oninit: () =>{
Data.refreshGpgDetails();
//rs.setBackgroundTask(
// Data.refreshGpgDetails,
// 10000,
// () => m.route.get() === '/network',
//)
},
view: () => m('.widget', [
m('h3', 'Friend nodes'),
m('hr'),
Object.keys(FriendNodes).map((id) => m(Node, {
data: FriendNodes[id].locations,
isSearched: FriendNodes[id].isSearched,
})),
]),
view: () =>
m('.widget', [
m('h3', 'Friend nodes'),
m('hr'),
Object.keys(Data.gpgDetails).map(id => m(Friend, {id})),
]),
};
};
const Layout = () => {
return {
view: () => m('.tab-page', [
m(SearchBar),
m(Friends),
])
}
view: () => m('.tab-page', [m(SearchBar), m(FriendsList)]),
};
};
module.exports = Layout;

View File

@ -0,0 +1,70 @@
const rs = require('rswebui');
const Data = {
gpgDetails: {},
refreshGpgDetails,
};
async function refreshIds() {
let sslIds = [];
await rs.rsJsonApiRequest(
'/rsPeers/getFriendList',
{},
data => (sslIds = data.sslIds),
);
return sslIds;
}
async function loadSslDetails() {
let sslDetails = [];
let sslIds = await refreshIds();
await Promise.all(
sslIds.map(sslId =>
rs.rsJsonApiRequest('/rsPeers/getPeerDetails', {sslId}, data =>
sslDetails.push(data.det),
),
),
);
return sslDetails;
}
async function refreshGpgDetails() {
let details = {};
let sslDetails = await loadSslDetails();
await Promise.all(
sslDetails.map(data => {
let isOnline = false;
return rs
.rsJsonApiRequest(
'/rsPeers/isOnline',
{sslId: data.id},
stat => (isOnline = stat.retval),
)
.then(() => {
loc = {
name: data.location,
id: data.id,
lastSeen: data.lastConnect,
isOnline,
gpg_id: data.gpg_id,
};
if (details[data.gpg_id] === undefined) {
details[data.gpg_id] = {
name: data.name,
isSearched: true,
isOnline,
locations: [loc],
};
} else {
details[data.gpg_id].locations.push(loc);
}
details[data.gpg_id].isOnline =
details[data.gpg_id].isOnline || isOnline;
});
}),
);
Data.gpgDetails = details;
}
module.exports = Data;