Merge pull request #57 from Sukhamjot-Singh/channels

Forums tab 3
This commit is contained in:
csoler 2022-09-13 22:24:13 +02:00 committed by GitHub
commit 27169dcfe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1592 additions and 348 deletions

BIN
data/streaming.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -97,7 +97,15 @@ table.comments td:nth-child(2) {
table.comments tr {
height: 40px;
}
table.files th:nth-child(1) {
width: 40%;
}
table.files th:nth-child(3) {
width: 50%;
}
table.files td {
word-wrap: break-word;
}
/* #options{
width: 100px;
text-align: center;
@ -105,3 +113,10 @@ table.comments tr {
margin-left: 20px;
height: 40px;
} */
#mtags {
width: 160px;
text-align: center;
font-size: medium;
margin-left: 10px;
height: 40px;
}

View File

@ -1,6 +1,7 @@
const m = require('mithril');
const rs = require('rswebui');
const util = require('channels/channels_util');
const widget = require('widgets');
const Data = util.Data;
const peopleUtil = require('people/people_util');
const sha1 = require('channels/sha1');
@ -72,15 +73,164 @@ async function parsefile(file, type) {
await chunkreaderblock(offset, chunkSize, file);
return ansList;
}
const messageGroups = ['Public', 'Restricted Circle', 'Restricted Node Group'];
const messageGroupsCode = [util.PUBLIC, util.EXTERNAL, util.NODES_GROUP]; //rsgxscirles.h:50
function createchannel() {
let title;
let body;
let identity;
let thumbnail;
let selectedGroup = messageGroups[0];
let selectedGroupCode = messageGroupsCode[0];
let selectedCircle;
let circles;
return {
oninit: async (vnode) => {
if (vnode.attrs.authorId) {
identity = vnode.attrs.authorId[0];
}
const res = await rs.rsJsonApiRequest('/rsgxscircles/getCirclesSummaries');
if (res.body.retval) {
circles = res.body.circles;
selectedCircle = circles[0].mGroupName;
}
},
view: (vnode) =>
m('.widget', [
m('h3', 'Create Channel'),
m('hr'),
m('input[type=text][placeholder=Title]', {
style: { float: 'left' },
oninput: (e) => (title = e.target.value),
}),
m('div', { style: { float: 'right', marginTop: '10px', marginBottom: '10px' } }, [
m('label[for=thumbnail]', 'Thumbnail: '),
m('input[type=file][name=files][id=thumbnail][accept=image/*]', {
onchange: async (e) => {
let reader = new FileReader();
reader.onloadend = function () {
thumbnail = reader.result.substring(reader.result.indexOf(',') + 1);
};
reader.readAsDataURL(e.target.files[0]);
},
}),
]),
m('div', { style: { float: 'right', marginTop: '10px', marginBottom: '10px' } }, [
m('label[for=idtags]', 'Select identity: '),
m(
'select[id=idtags]',
{
value: identity,
onchange: (e) => {
identity = vnode.attrs.authorId[e.target.selectedIndex];
},
},
[
vnode.attrs.authorId &&
vnode.attrs.authorId.map((o) =>
m(
'option',
{ value: o },
rs.userList.userMap[o]
? rs.userList.userMap[o].toLocaleString() + ' (' + o.slice(0, 8) + '...)'
: 'No Signature'
)
),
]
),
]),
m('div', { style: { float: 'left', marginTop: '10px', marginBottom: '10px' } }, [
m('label[for=mtags]', 'Message Distribution: '),
m(
'select[id=mtags]',
{
value: selectedGroup,
onchange: (e) => {
selectedGroup = messageGroups[e.target.selectedIndex];
selectedGroupCode = messageGroupsCode[e.target.selectedIndex];
widget.popupMessage(m(createchannel, { authorId: vnode.attrs.authorId }));
},
},
[messageGroups.map((group) => m('option', { value: group }, group))]
),
]),
circles &&
m(
'div',
{
style: {
float: 'left',
marginTop: '10px',
marginBottom: '10px',
display: selectedGroupCode === util.EXTERNAL ? 'block' : 'none',
},
},
[
m('label[for=circlestag]', 'Circles: '),
m(
'select[id=circlestag]',
{
value: selectedCircle,
onchange: (e) => {
selectedCircle = circles[e.target.selectedIndex];
console.log(selectedCircle);
// selectedGroupCode = messageGroupsCode[e.target.selectedIndex];
},
},
[
circles.map((circle) =>
m('option', { value: circle.mGroupName }, circle.mGroupName)
),
]
),
]
),
m('textarea[rows=5][placeholder=Description]', {
style: { width: '100%', display: 'block' },
oninput: (e) => (body = e.target.value),
value: body,
}),
m(
'button',
{
onclick: async () => {
const res = await rs.rsJsonApiRequest('/rsgxschannels/createChannelV2', {
name: title,
description: body,
thumbnail: { mData: { base64: thumbnail } },
...(Number(identity) !== 0 && { authorId: identity }), //checks if some identity has to be assigned
circleType: selectedGroupCode,
...(selectedGroupCode === util.EXTERNAL &&
selectedCircle && { circleId: selectedCircle.mGroupId }), //checks if the selectedGroup code is EXTERNAL
});
if (res.body.retval) {
util.updatedisplaychannels(res.body.channelId);
m.redraw();
}
res.body.retval === false
? widget.popupMessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: widget.popupMessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Channel created successfully'),
]);
},
},
'Create'
),
]),
};
}
const AddPost = () => {
let content = '';
let ptitle = '';
let pthumbnail = [];
let pfiles = [];
let uploadFiles = false;
let uploadThumbnail = false;
let uploadFiles = true;
return {
view: (vnode) =>
m('.widget', [
@ -89,23 +239,18 @@ const AddPost = () => {
m('label[for=thumbnail]', 'Thumbnail: '),
m('input[type=file][name=files][id=thumbnail][accept=image/*]', {
onchange: async (e) => {
filesUploadHashes.Thumbnail = [];
pthumbnail = [];
const ansList = await parsefile(e.target.files[0], '');
if (filesUploadHashes.Thumbnail.length === e.target.files.length) {
pthumbnail.push({
name: e.target.files[0].name,
size: e.target.files[0].size,
hash: filesUploadHashes.Thumbnail[0],
});
uploadThumbnail = true;
}
let reader = new FileReader();
reader.onloadend = function () {
pthumbnail = reader.result.substring(reader.result.indexOf(',') + 1);
};
reader.readAsDataURL(e.target.files[0]); // converts into base64 string
},
}),
m('label[for=browse]', 'Attachments: '),
m('input[type=file][name=files][id=browse][multiple=multiple]', {
// attachments option wrong hash, not working
onchange: async (e) => {
uploadFiles = false;
filesUploadHashes.PostFiles = [];
pfiles = [];
for (let i = 0; i < e.target.files.length; i++) {
@ -129,7 +274,7 @@ const AddPost = () => {
oninput: (e) => (ptitle = e.target.value),
}),
m('textarea[rows=5]', {
style: {width: '90%', display: 'block'},
style: { width: '90%', display: 'block' },
oninput: (e) => (content = e.target.value),
value: content,
}),
@ -137,22 +282,24 @@ const AddPost = () => {
'button',
{
onclick: async () => {
if (uploadFiles && uploadThumbnail) {
if (uploadFiles) {
console.log(vnode.attrs.chanId, ptitle, content, pfiles, pthumbnail);
const res = await rs.rsJsonApiRequest('/rsgxschannels/createPostV2', {
channelId: vnode.attrs.chanId,
title: ptitle,
mBody: content,
files: pfiles,
thumbnail: pthumbnail,
files: pfiles, //does not work for now
thumbnail: { mData: { base64: pthumbnail } },
});
res.body.retval === false
? util.m([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: util.m([
? widget.popupMessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: widget.popupMessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Post added successfully'),
]);
util.updatedisplaychannels(vnode.attrs.chanId);
m.redraw();
}
},
},
@ -167,6 +314,7 @@ const ChannelView = () => {
let cimage = '';
let cauthor = '';
let csubscribed = {};
let mychannel = false;
let cposts = 0;
let plist = {};
let createDate = {};
@ -184,6 +332,7 @@ const ChannelView = () => {
cauthor = 'Unknown';
}
csubscribed = Data.DisplayChannels[v.attrs.id].isSubscribed;
mychannel = Data.DisplayChannels[v.attrs.id].mychannel;
cposts = Data.DisplayChannels[v.attrs.id].posts;
createDate = Data.DisplayChannels[v.attrs.id].created;
lastActivity = Data.DisplayChannels[v.attrs.id].activity;
@ -228,7 +377,10 @@ const ChannelView = () => {
csubscribed ? 'Subscribed' : 'Subscribe'
),
m('img.channelpic', {
src: 'data:image/png;base64,' + cimage.mData.base64,
src:
cimage.mData.base64 === ''
? 'data/streaming.png'
: 'data:image/png;base64,' + cimage.mData.base64,
}),
m('[id=channeldetails]', [
m('p', m('b', 'Posts: '), cposts),
@ -257,10 +409,12 @@ const ChannelView = () => {
style: 'display:' + (csubscribed ? 'block' : 'none'),
},
m('h3', 'Posts'),
m('button', { onclick: () => util.m(m(AddPost, { chanId: v.attrs.id })) }, [
'Add Post',
m('i.fas.fa-edit'),
]),
mychannel &&
m(
'button',
{ onclick: () => widget.popupMessage(m(AddPost, { chanId: v.attrs.id })) },
['Add Post', m('i.fas.fa-edit')]
),
m('hr'),
m(
@ -270,7 +424,7 @@ const ChannelView = () => {
'div',
{
class: 'card',
style: 'display: ' + (plist[key].isSearched ? 'block' : 'none'),
style: 'display: ' + (plist[key].isSearched ? 'block' : 'none'), // for search
onclick: () => {
m.route.set('/channels/:tab/:mGroupId/:mMsgId', {
tab: m.route.param().tab,
@ -282,7 +436,10 @@ const ChannelView = () => {
[
m('img', {
class: 'card-img',
src: 'data:image/png;base64,' + plist[key].post.mThumbnail.mData.base64,
src:
plist[key].post.mThumbnail.mData.base64 === ''
? 'data/streaming.png'
: 'data:image/png;base64,' + plist[key].post.mThumbnail.mData.base64,
alt: '',
}),
@ -315,16 +472,42 @@ async function addvote(voteType, vchannelId, vpostId, vauthorId, vcommentId) {
const AddComment = () => {
let inputComment = '';
let identity;
return {
oninit: (vnode) => {
if (vnode.attrs.authorId) {
identity = vnode.attrs.authorId[0];
}
},
view: (vnode) =>
m('.widget', [
m('h3', 'Add Comment'),
m('label[for=tags]', 'Select identity'),
m(
'select[id=idtags]',
{
value: identity,
onchange: (e) => {
identity = vnode.attrs.authorId[e.target.selectedIndex];
},
},
[
vnode.attrs.authorId &&
vnode.attrs.authorId.map((o) =>
m(
'option',
{ value: o },
rs.userList.userMap[o].toLocaleString() + ' (' + o.slice(0, 8) + '...)'
)
),
]
),
m('hr'),
(vnode.attrs.parent_comment !== '') > 0
? [m('h5', 'Reply to comment: '), m('p', vnode.attrs.parent_comment)]
? [m('h5', 'Reply to comment: '), m('p', vnode.attrs.parent_comment)] // if it is add reply option
: '',
m('textarea[rows=5]', {
style: {width: '90%', display: 'block'},
style: { width: '90%', display: 'block' },
oninput: (e) => (inputComment = e.target.value),
value: inputComment,
}),
@ -336,13 +519,13 @@ const AddComment = () => {
channelId: vnode.attrs.channelId,
threadId: vnode.attrs.threadId,
comment: inputComment,
authorId: vnode.attrs.authorId,
authorId: identity,
parentId: vnode.attrs.parentId,
});
res.body.retval === false
? util.m([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: util.m([
? widget.popupMessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: widget.popupMessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Comment added successfully'),
@ -357,9 +540,10 @@ const AddComment = () => {
};
};
function displaycomment() {
// recursive function to display comments
return {
oninit: (v) => {},
view: ({ attrs: { commentStruct, identity, replyDepth } }) => {
view: ({ attrs: { commentStruct, identity, replyDepth, voteIdentity } }) => {
const comment = commentStruct.comment;
let parMap = {};
if (Data.ParentCommentMap[comment.mMeta.mMsgId]) {
@ -367,12 +551,12 @@ function displaycomment() {
}
return [
m('tr', [
Object.keys(parMap).length
Object.keys(parMap).length // if it has replies
? m(
'td',
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (commentStruct.showReplies ? '90' : '0'),
style: 'margin-top:12px',
style: 'cursor:pointer',
onclick: () => {
commentStruct.showReplies = !commentStruct.showReplies;
},
@ -386,7 +570,7 @@ function displaycomment() {
style: {
position: 'relative',
'--replyDepth': replyDepth,
left: 'calc(30px*var(--replyDepth))',
left: 'calc(30px*var(--replyDepth))', // shifts the reply by 30px
},
},
[
@ -397,7 +581,7 @@ function displaycomment() {
{
style: 'font-size:15px',
onclick: () =>
util.m(
widget.popupMessage(
m(AddComment, {
parent_comment: comment.mComment,
channelId: comment.mMeta.mGroupId,
@ -409,36 +593,38 @@ function displaycomment() {
},
'Reply'
),
m(
'button',
{
style: 'font-size:15px',
onclick: () =>
addvote(
util.GXS_VOTE_UP,
comment.mMeta.mGroupId,
comment.mMeta.mThreadId,
identity,
comment.mMeta.mMsgId
),
},
m('i.fas.fa-thumbs-up')
),
m(
'button',
{
style: 'font-size:15px',
onclick: () =>
addvote(
util.GXS_VOTE_DOWN,
comment.mMeta.mGroupId,
comment.mMeta.mThreadId,
identity,
comment.mMeta.mMsgId
),
},
m('i.fas.fa-thumbs-down')
),
voteIdentity &&
m(
'button',
{
style: 'font-size:15px',
onclick: () =>
addvote(
util.GXS_VOTE_UP,
comment.mMeta.mGroupId,
comment.mMeta.mThreadId,
voteIdentity,
comment.mMeta.mMsgId
),
},
m('i.fas.fa-thumbs-up')
),
voteIdentity &&
m(
'button',
{
style: 'font-size:15px',
onclick: () =>
addvote(
util.GXS_VOTE_DOWN,
comment.mMeta.mGroupId,
comment.mMeta.mThreadId,
voteIdentity,
comment.mMeta.mMsgId
),
},
m('i.fas.fa-thumbs-down')
),
]),
]
),
@ -454,13 +640,14 @@ function displaycomment() {
m('td', comment.mUpVotes),
m('td', comment.mDownVotes),
]),
commentStruct.showReplies &&
commentStruct.showReplies && // recursive calls for the replies
// parMap.map((value) =>
Object.keys(parMap).map((key, index) =>
m(displaycomment, {
commentStruct: Data.Comments[parMap[key].mMeta.mThreadId][parMap[key].mMeta.mMsgId],
voteIdentity,
identity,
replyDepth: replyDepth + 1,
replyDepth: replyDepth + 1, // for the css
})
),
];
@ -472,26 +659,35 @@ const PostView = () => {
let post = {};
let topComments = {};
const filesInfo = {};
let ownId = {};
let voteIdentity;
let ownId;
return {
oninit: (v) => {
oninit: async (v) => {
if (Data.Posts[v.attrs.channelId] && Data.Posts[v.attrs.channelId][v.attrs.msgId]) {
post = Data.Posts[v.attrs.channelId][v.attrs.msgId].post;
}
if (Data.TopComments[v.attrs.msgId]) {
topComments = Data.TopComments[v.attrs.msgId];
topComments = Data.TopComments[v.attrs.msgId]; // get all the top level parent comments
}
if (post) {
post.mFiles.map(async (file) => {
const res = await rs.rsJsonApiRequest('/rsfiles/alreadyHaveFile', {
// checks if the file is already there with the user
hash: file.mHash,
});
filesInfo[file.mHash] = res.body;
});
}
peopleUtil.ownIds((data) => {
ownId = data[0];
await peopleUtil.ownIds((data) => {
ownId = data;
for (let i = 0; i < ownId.length; i++) {
if (Number(ownId[i]) === 0) {
ownId.splice(i, 1); // workaround for id '0'
}
}
voteIdentity = ownId[0];
});
fileDown.Downloads.loadStatus(); // for retrieving downloading files.
},
view: (v) =>
m('.widget', { key: v.attrs.msgId }, [
@ -521,20 +717,55 @@ const PostView = () => {
m(
'button',
{
onclick: async () => {
filesInfo[file.mHash]
? filesInfo[file.mHash].retval
? ''
: await rs.rsJsonApiRequest('/rsFiles/FileRequest', {
fileName: file.mName,
hash: file.mHash,
flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING,
size: {
xstr64: file.mSize.xstr64,
},
})
: '';
},
style: { fontSize: '0.9em' },
onclick: async () =>
widget.popupMessage([
m('p', 'Start Download?'),
m(
'button',
{
onclick: async () => {
if (filesInfo[file.mHash] && !filesInfo[file.mHash].retval) {
const res = await rs.rsJsonApiRequest('/rsFiles/FileRequest', {
fileName: file.mName,
hash: file.mHash,
flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING,
size: {
xstr64: file.mSize.xstr64,
},
});
res.body.retval === false
? widget.popupMessage([
m('h3', 'Error'),
m('hr'),
m('p', res.body.errorMessage),
])
: widget.popupMessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Download Started'),
]);
m.redraw();
}
},
},
'Start Download'
),
]),
// {
// filesInfo[file.mHash]
// ? filesInfo[file.mHash].retval
// ? ''
// : (await rs.rsJsonApiRequest('/rsFiles/FileRequest', {
// fileName: file.mName,
// hash: file.mHash,
// flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING,
// size: {
// xstr64: file.mSize.xstr64,
// },
// })) && m.redraw()
// : '';
// },
},
filesInfo[file.mHash]
? filesInfo[file.mHash].retval
@ -542,7 +773,7 @@ const PostView = () => {
: ['Download', m('i.fas.fa-download')]
: 'Please Wait...'
),
fileDown.list[file.mHash] &&
fileDown.list[file.mHash] && // using the file from files_util to display download.
m(fileUtil.File, {
info: fileDown.list[file.mHash],
direction: 'down',
@ -559,7 +790,7 @@ const PostView = () => {
'button',
{
onclick: () => {
util.popupmessage(
widget.popupMessage(
m(AddComment, {
parent_comment: '',
channelId: v.attrs.channelId,
@ -572,6 +803,32 @@ const PostView = () => {
},
'Add Comment'
),
m(
'label[for=idtags',
{
style: { marginLeft: '10px' },
},
'Voter ID: '
),
m(
'select[id=idtags]',
{
value: voteIdentity,
onchange: (e) => {
voteIdentity = ownId[e.target.selectedIndex];
},
},
[
ownId &&
ownId.map((o) =>
m(
'option',
{ value: o },
rs.userList.userMap[o].toLocaleString() + ' (' + o.slice(0, 8) + '...)'
)
),
]
),
m(
util.CommentsTable,
m(
@ -580,7 +837,9 @@ const PostView = () => {
Data.Comments[topComments[key].mMeta.mThreadId] &&
Data.Comments[topComments[key].mMeta.mThreadId][topComments[key].mMeta.mMsgId]
? m(displaycomment, {
// calls the recursive function for all the parents.
identity: ownId,
voteIdentity,
commentStruct:
Data.Comments[topComments[key].mMeta.mThreadId][
topComments[key].mMeta.mMsgId
@ -598,4 +857,5 @@ const PostView = () => {
module.exports = {
ChannelView,
PostView,
createchannel,
};

View File

@ -3,22 +3,31 @@ const widget = require('widgets');
const rs = require('rswebui');
const util = require('channels/channels_util');
const viewUtil = require('channels/channel_view');
const peopleUtil = require('people/people_util');
const getChannels = {
All: [],
PopularChannels: [],
SubscribedChannels: [],
MyChannels: [],
OtherChannels: [],
async load() {
const res = await rs.rsJsonApiRequest('/rsgxschannels/getChannelsSummaries');
const data = res.body;
getChannels.All = data.channels;
getChannels.PopularChannels = getChannels.All;
getChannels.SubscribedChannels = getChannels.All.filter(
(channel) =>
channel.mSubscribeFlags === util.GROUP_SUBSCRIBE_SUBSCRIBED ||
channel.mSubscribeFlags === util.GROUP_MY_CHANNEL
channel.mSubscribeFlags === util.GROUP_MY_CHANNEL // my channel is subscribed
);
// getChannels.PopularChannels = getChannels.All;
getChannels.PopularChannels = getChannels.All.filter(
(a) => !getChannels.SubscribedChannels.includes(a)
);
getChannels.PopularChannels.sort((a, b) => b.mPop - a.mPop);
getChannels.OtherChannels = getChannels.PopularChannels.slice(5);
getChannels.PopularChannels = getChannels.PopularChannels.slice(0, 5);
getChannels.MyChannels = getChannels.All.filter(
(channel) => channel.mSubscribeFlags === util.GROUP_MY_CHANNEL
);
@ -32,46 +41,74 @@ const sections = {
OtherChannels: require('channels/other_channels'),
};
const Layout = {
oninit: () => {
rs.setBackgroundTask(getChannels.load, 5000, () => {
// return m.route.get() === '/files/files';
});
},
// onupdate: getChannels.load,
view: (vnode) =>
m('.tab-page', [
Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mMsgId')
? ''
: Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mGroupId')
? m(util.SearchBar, {
category: 'posts',
channelId: vnode.attrs.pathInfo.mGroupId,
})
: m(util.SearchBar, {
category: 'channels',
}),
m(widget.Sidebar, {
tabs: Object.keys(sections),
baseRoute: '/channels/',
}),
m(
'.channel-node-panel',
const Layout = () => {
let ownId;
return {
oninit: () => {
rs.setBackgroundTask(getChannels.load, 5000, () => {
// return m.route.get() === '/files/files';
});
peopleUtil.ownIds((data) => {
ownId = data;
for (let i = 0; i < ownId.length; i++) {
if (Number(ownId[i]) === 0) {
ownId.splice(i, 1);
}
}
ownId.unshift(0); // we need an extra check when a channel is created with no identity.
});
},
// onupdate: getChannels.load,
view: (vnode) =>
m('.tab-page', [
Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mMsgId')
? m(viewUtil.PostView, {
msgId: vnode.attrs.pathInfo.mMsgId,
? ''
: Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mGroupId')
? m(util.SearchBar, {
category: 'posts',
channelId: vnode.attrs.pathInfo.mGroupId,
})
: Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mGroupId')
? m(viewUtil.ChannelView, {
id: vnode.attrs.pathInfo.mGroupId,
})
: m(sections[vnode.attrs.pathInfo.tab], {
list: getChannels[vnode.attrs.pathInfo.tab],
})
),
]),
: m(util.SearchBar, {
category: 'channels',
}),
m(
'button',
{
style: { fontSize: '1.2em', width: '200px' },
onclick: () =>
ownId &&
widget.popupMessage(
m(viewUtil.createchannel, {
authorId: ownId,
})
),
},
'Create Channel'
),
m(widget.Sidebar, {
tabs: Object.keys(sections),
baseRoute: '/channels/',
}),
m(
'.channel-node-panel',
Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mMsgId') // posts
? m(viewUtil.PostView, {
msgId: vnode.attrs.pathInfo.mMsgId,
channelId: vnode.attrs.pathInfo.mGroupId,
})
: Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mGroupId') // channels view
? m(viewUtil.ChannelView, {
id: vnode.attrs.pathInfo.mGroupId,
})
: m(sections[vnode.attrs.pathInfo.tab], {
// subscribed, all, popular, other
list: getChannels[vnode.attrs.pathInfo.tab],
})
),
]),
};
};
module.exports = {

View File

@ -1,22 +1,34 @@
const m = require('mithril');
const rs = require('rswebui');
const GROUP_SUBSCRIBE_ADMIN = 0x01; // means: you have the admin key for this group
const GROUP_SUBSCRIBE_PUBLISH = 0x02; // means: you have the publish key for thiss group. Typical use: publish key in channels are shared with specific friends.
const GROUP_SUBSCRIBE_SUBSCRIBED = 0x04; // means: you are subscribed to a group, which makes you a source for this group to your friend nodes.
// rstypes.h:96
const GROUP_SUBSCRIBE_ADMIN = 0x01; // means: you have the admin key for this group
const GROUP_SUBSCRIBE_PUBLISH = 0x02; // means: you have the publish key for thiss group. Typical use: publish key in channels are shared with specific friends.
const GROUP_SUBSCRIBE_SUBSCRIBED = 0x04; // means: you are subscribed to a group, which makes you a source for this group to your friend nodes.
const GROUP_SUBSCRIBE_NOT_SUBSCRIBED = 0x08;
const GROUP_MY_CHANNEL =
GROUP_SUBSCRIBE_ADMIN + GROUP_SUBSCRIBE_SUBSCRIBED + GROUP_SUBSCRIBE_PUBLISH;
// rsfiles.h:168
const RS_FILE_REQ_ANONYMOUS_ROUTING = 0x00000040;
// rsgxscommon.h:194
const GXS_VOTE_DOWN = 0x0001;
const GXS_VOTE_UP = 0x0002;
//rsgxscircles.h:50
const PUBLIC = 1; /// Public distribution
const EXTERNAL = 2; /// Restricted to an external circle, based on GxsIds
const NODES_GROUP = 3;
const Data = {
DisplayChannels: {},
Posts: {},
Comments: {},
TopComments: {},
ParentCommentMap: {},
DisplayChannels: {}, // chanID -> channel info
Posts: {}, // chanID, PostID -> {post, isSearched}
Comments: {}, // threadID, msgID -> {Comment, showReplies}
TopComments: {}, // threadID, msgID -> comment(Top thread comment)
ParentCommentMap: {}, // stores replies of a comment threadID, msgID -> comment
Votes: {},
};
async function updatecontent(content, channelid) {
@ -33,13 +45,15 @@ async function updatecontent(content, channelid) {
Data.Comments[content.mThreadId][content.mMsgId] = {
comment: res.body.comments[0],
showReplies: false,
}; // Comments[post][comment]
}; // Comments[post][comment]
const comm = res.body.comments[0];
if (Data.TopComments[comm.mMeta.mThreadId] === undefined) {
Data.TopComments[comm.mMeta.mThreadId] = {};
}
if (comm.mMeta.mThreadId === comm.mMeta.mParentId) {
Data.TopComments[comm.mMeta.mThreadId][comm.mMeta.mMsgId] = comm; // pushing top comments respective to post
// this is a check for the top level comments
Data.TopComments[comm.mMeta.mThreadId][comm.mMeta.mMsgId] = comm;
// pushing top comments respective to post
} else {
if (Data.ParentCommentMap[comm.mMeta.mParentId] === undefined) {
Data.ParentCommentMap[comm.mMeta.mParentId] = {};
@ -48,17 +62,23 @@ async function updatecontent(content, channelid) {
}
} else if (res.body.retval && res.body.votes.length > 0) {
const vote = res.body.votes[0];
if (
Data.Comments[vote.mMeta.mThreadId] &&
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId] // finding [post][comment]
) {
if (vote.mVoteType === GXS_VOTE_UP) {
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].comment.mUpVotes += 1;
}
if (vote.mVoteType === GXS_VOTE_DOWN) {
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].comment.mDownVotes += 1;
}
}
Data.Votes[vote.mMeta.mMsgId] = vote;
// console.log(vote);
// if (
// Data.Comments[vote.mMeta.mThreadId] &&
// Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId] // finding [post][comment]
// ) {
// console.log(Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].comment);
// if (vote.mVoteType === GXS_VOTE_UP) {
// console.log(Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].comment.mUpVotes);
// Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].comment.mUpVotes += 1;
// console.log(Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].comment.mUpVotes);
// }
// if (vote.mVoteType === GXS_VOTE_DOWN) {
// Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].comment.mDownVotes += 1;
// }
// }
}
}
@ -68,6 +88,7 @@ async function updatedisplaychannels(keyid, details) {
});
details = res1.body.channelsInfo[0];
Data.DisplayChannels[keyid] = {
// struct for a channel
name: details.mMeta.mGroupName,
isSearched: true,
description: details.mDescription,
@ -76,6 +97,7 @@ async function updatedisplaychannels(keyid, details) {
isSubscribed:
details.mMeta.mSubscribeFlags === GROUP_SUBSCRIBE_SUBSCRIBED ||
details.mMeta.mSubscribeFlags === GROUP_MY_CHANNEL,
mychannel: details.mMeta.mSubscribeFlags === GROUP_MY_CHANNEL,
posts: details.mMeta.mVisibleMsgCount,
activity: details.mMeta.mLastPost,
created: details.mMeta.mPublishTs,
@ -89,10 +111,36 @@ async function updatedisplaychannels(keyid, details) {
});
if (res2.body.retval) {
res2.body.summaries.map((content) => {
updatecontent(content, keyid);
res2.body.summaries.map(async (content) => {
await updatecontent(content, keyid);
});
}
console.log(Data.Votes);
// Data.Votes.length > 1 &&
Object.keys(Data.Votes).map((key, index) => {
console.log('hello');
if (
Data.Comments[Data.Votes[key].mMeta.mThreadId] &&
Data.Comments[Data.Votes[key].mMeta.mThreadId][Data.Votes[key].mMeta.mParentId] // finding [post][comment]
) {
console.log(
Data.Comments[Data.Votes[key].mMeta.mThreadId][Data.Votes[key].mMeta.mParentId].comment
);
if (Data.Votes[key].mVoteType === GXS_VOTE_UP) {
// console.log(Data.Comments[Data.Votes[key].mMeta.mThreadId][Data.Votes[key].mMeta.mParentId].comment.mUpVotes);
Data.Comments[Data.Votes[key].mMeta.mThreadId][
Data.Votes[key].mMeta.mParentId
].comment.mUpVotes += 1;
// console.log(Data.Comments[Data.Votes[key].mMeta.mThreadId][Data.Votes[key].mMeta.mParentId].comment.mUpVotes);
}
if (Data.Votes[key].mVoteType === GXS_VOTE_DOWN) {
Data.Comments[Data.Votes[key].mMeta.mThreadId][
Data.Votes[key].mMeta.mParentId
].comment.mDownVotes += 1;
}
}
});
}
const DisplayChannelsFromList = () => {
return {
@ -150,7 +198,6 @@ const CommentsTable = () => {
};
};
const FilesTable = () => {
return {
oninit: (v) => {},
@ -163,6 +210,7 @@ const FilesTable = () => {
};
function formatbytes(bytes, decimals = 2) {
// takes in size returns a string (size)(kb/gb/tb)
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
@ -171,24 +219,6 @@ function formatbytes(bytes, decimals = 2) {
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function popupmessage(message) {
const container = document.getElementById('modal-container');
container.style.display = 'block';
m.render(
container,
m('.modal-content', [
m(
'button.red',
{
onclick: () => (container.style.display = 'none'),
},
m('i.fas.fa-times')
),
message,
])
);
}
const ChannelTable = () => {
return {
oninit: (v) => {},
@ -196,6 +226,7 @@ const ChannelTable = () => {
};
};
const SearchBar = () => {
// same search bar is used for both channels and posts
let searchString = '';
return {
view: (v) =>
@ -206,6 +237,7 @@ const SearchBar = () => {
oninput: (e) => {
searchString = e.target.value.toLowerCase();
if (v.attrs.category.localeCompare('channels') === 0) {
// for channels
for (const hash in Data.DisplayChannels) {
if (Data.DisplayChannels[hash].name.toLowerCase().indexOf(searchString) > -1) {
Data.DisplayChannels[hash].isSearched = true;
@ -215,6 +247,7 @@ const SearchBar = () => {
}
} else {
for (const hash in Data.Posts[v.attrs.channelId]) {
// for posts
if (
Data.Posts[v.attrs.channelId][hash].post.mMeta.mMsgName
.toLowerCase()
@ -234,7 +267,6 @@ const SearchBar = () => {
module.exports = {
Data,
SearchBar,
popupmessage,
ChannelSummary,
formatbytes,
DisplayChannelsFromList,
@ -249,5 +281,8 @@ module.exports = {
GROUP_MY_CHANNEL,
GXS_VOTE_DOWN,
GXS_VOTE_UP,
PUBLIC,
EXTERNAL,
NODES_GROUP,
RS_FILE_REQ_ANONYMOUS_ROUTING,
};

View File

@ -1,9 +1,32 @@
const m = require('mithril');
const util = require('channels/channels_util');
const Layout = () => {
return {
view: () => [m('.widget', [m('h2', 'Other Channels'), m('hr')])],
view: (v) => [
m('.widget', [
m('h3', 'Other Channels'),
m('hr'),
m(
util.ChannelTable,
m('tbody', [
v.attrs.list.map((channel) =>
m(util.ChannelSummary, {
details: channel,
category: 'OtherChannels',
})
),
v.attrs.list.map((channel) =>
m(util.DisplayChannelsFromList, {
id: channel.mGroupId,
category: 'OtherChannels',
})
),
])
),
]),
],
};
};
module.exports = Layout();
module.exports = Layout;

View File

@ -0,0 +1,6 @@
.config-node-panel {
position: relative;
bottom: 250px;
margin-left: 200px;
animation: fadein 0.5s;
}

View File

@ -6,28 +6,16 @@ const util = require('config/config_util'); // for future use
/* eslint-disable no-unused-vars */
const SetNwMode = () => {
let mode = undefined;
let sslId = undefined;
const setMode = () => rs.rsJsonApiRequest('/');
let sslId;
return {
oninit: util.getSslId().then((val) => {
sslId = val;
rs.rsJsonApiRequest('/');
}),
oninit: async () => {
// const res = await rs.rsJsonApiRequest('/rsPeers/getOwnId');
console.log(util.getSslId());
},
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)
)
),
],
};
};
@ -130,7 +118,7 @@ const Component = () => {
m('.grid-2col', [
m(SetLimits),
m(SetOpMode),
// m(SetNwMode),
m(SetNwMode),
]),
]),
};

View File

@ -17,7 +17,7 @@ const Layout = {
tabs: Object.keys(sections),
baseRoute: '/config/',
}),
m('.node-panel', vnode.children),
m('.config-node-panel', vnode.children),
]),
};

View File

@ -22,6 +22,14 @@
float: right;
margin-left: 15px;
}
.file-view select {
float: right;
margin-left: 15px;
}
.file-view label {
float: right;
margin-left: 15px;
}
span > i {
margin-right: 5px;
}
@ -36,3 +44,37 @@ span > i {
width: 40%;
box-shadow: 0 0 3px #aaa;
}
.file-node-panel {
position: relative;
bottom: 200px;
margin-left: 200px;
animation: fadein 0.5s;
}
table.myfiles td {
word-wrap: break-word;
}
table.myfiles th:nth-child(1) {
width: 2%;
}
table.myfiles th:nth-child(2) {
width: 50%;
}
table.myfiles td:nth-child(2) {
text-align: start;
}
table.friendsfiles td {
word-wrap: break-word;
}
table.friendsfiles th:nth-child(1) {
width: 2%;
}
table.friendsfiles th:nth-child(2) {
width: 50%;
}
table.friendsfiles th:nth-child(4) {
width: 40%;
}
table.friendsfiles td:nth-child(2) {
text-align: start;
}

View File

@ -7,14 +7,18 @@ const Downloads = {
statusMap: {},
hashes: [],
loadHashes() {
rs.rsJsonApiRequest('/rsFiles/FileDownloads', {}, (d) => (Downloads.hashes = d.hashs));
async loadHashes() {
const res = await rs.rsJsonApiRequest(
'/rsFiles/FileDownloads',
{},
(d) => (Downloads.hashes = d.hashs)
);
},
loadStatus() {
Downloads.loadHashes();
async loadStatus() {
await Downloads.loadHashes();
const fileKeys = Object.keys(Downloads.statusMap);
if (Downloads.hashes.length !== fileKeys.length) {
if (Downloads.hashes !== undefined && Downloads.hashes.length !== fileKeys.length) {
// New file added
if (Downloads.hashes.length > fileKeys.length) {
const newHashes = util.compareArrays(Downloads.hashes, fileKeys);
@ -165,5 +169,6 @@ const Component = () => {
module.exports = {
Component,
Downloads,
list: Downloads.statusMap,
};

View File

@ -6,6 +6,8 @@ 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 myfile = require('files/my_files');
const friendfile = require('files/friends_files');
const MyFiles = () => {
return {
@ -22,6 +24,8 @@ const MyFiles = () => {
const sections = {
files: MyFiles,
search,
MyFiles: myfile,
FriendsFiles: friendfile,
};
const Layout = {
@ -31,7 +35,7 @@ const Layout = {
tabs: Object.keys(sections),
baseRoute: '/files/',
}),
m('.node-panel', vnode.children),
m('.file-node-panel', vnode.children),
]),
};

View File

@ -3,26 +3,33 @@ const rs = require('rswebui');
let matchString = '';
const SearchBar = () => {
let headers = {};
return {
oninit: () => {
headers['Accept'] = 'application/json';
if (rs.loginKey.isVerified) {
headers['Authorization'] = 'Basic ' + btoa(rs.loginKey.username + ':' + rs.loginKey.passwd);
}
},
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);
}
);
let source = new EventSource('http://127.0.0.1:9092/rsFiles/turtleSearchRequest', {
headers,
// withCredentials: true,
});
// rs.rsJsonApiRequest(
// '/rsfiles/turtleSearchRequest',
// {
// matchString,
// },
// (data, stat) => {
// console.log('got: ', stat, ' data: ', data);
// }
// );
},
}),
};

View File

@ -1,5 +1,6 @@
const m = require('mithril');
const rs = require('rswebui');
const widget = require('widgets');
const RS_FILE_CTRL_PAUSE = 0x00000100;
const RS_FILE_CTRL_START = 0x00000200;
@ -15,6 +16,7 @@ const FT_STATE_PAUSED = 0x0006;
const FT_STATE_CHECKING_HASH = 0x0007;
const RS_FILE_REQ_ANONYMOUS_ROUTING = 0x00000040;
const RS_FILE_HINTS_REMOTE = 0x00000008;
function makeFriendlyUnit(bytes) {
let cnt = bytes;
@ -52,7 +54,7 @@ function calcRemainingTime(bytes, rate) {
}
}
function fileAction(hash, action) {
async function fileAction(hash, action) {
let actionHeader = '';
const jsonParams = {
hash,
@ -82,7 +84,8 @@ function fileAction(hash, action) {
console.error('Unknown action in Downloads.control()');
return;
}
rs.rsJsonApiRequest(actionHeader, jsonParams, () => {});
const res = await rs.rsJsonApiRequest(actionHeader, jsonParams, () => {});
return res.body.retval;
}
function actionButton(file, action) {
@ -120,7 +123,28 @@ function actionButton(file, action) {
title: 'cancel',
onclick() {
fileAction(file.hash, 'cancel');
widget.popupMessage(
m('Cancelpop', [
m('p', 'Are you sure you want to cancel download?'),
m(
'button',
{
onclick: async () => {
const res = await fileAction(file.hash, 'cancel');
if (res) {
widget.popupMessage(m('p', 'Download Cancelled Successfully'));
} else {
widget.popupMessage(m('p', 'Download Cancel Failed'));
}
m.redraw();
},
},
'Cancel'
),
])
);
// fileAction(file.hash, 'cancel');
},
},
m('i.fas.fa-times')
@ -151,7 +175,16 @@ const ProgressBar = () => {
};
};
const chunkStrats = [
'CHUNK_STRATEGY_STREAMING',
'CHUNK_STRATEGY_RANDOM',
'CHUNK_STRATEGY_PROGRESSIVE',
];
const chunkStratsOptions = ['Streaming', 'Random', 'Progressive'];
// rstypes.h :: 366
const File = () => {
let chunkStrat;
return {
view: (v) =>
m(
@ -172,12 +205,30 @@ const File = () => {
v.attrs.info,
v.attrs.info.downloadStatus === FT_STATE_PAUSED ? 'resume' : 'pause'
),
m(
'select[id=chunkTag]',
{
value: chunkStrat,
onchange: async (e) => {
chunkStrat = chunkStrats[e.target.selectedIndex];
const res = await rs.rsJsonApiRequest('/rsFiles/setChunkStrategy', {
hash: v.attrs.info.hash,
newStrategy: chunkStrat,
});
},
},
[chunkStratsOptions.map((opt) => m('option', { value: opt }, opt))]
),
m('label[for=chunkTag]', 'Set Chunk Strategy: '),
],
v.attrs.direction === 'up'
? []
: m(ProgressBar, {
rate: (v.attrs.transferred / v.attrs.info.size.xint64) * 100,
}),
m('span.filestat', m('i.fas.fa-download'), makeFriendlyUnit(v.attrs.transferred)),
m('span.filestat', m('i.fas.fa-file'), makeFriendlyUnit(v.attrs.info.size.xint64)),
m(
'span.filestat',
@ -208,7 +259,7 @@ const SearchBar = () => {
let searchString = '';
return {
view: (v) =>
m('input[type=text][placeholder=search].searchbar', {
m('input[type=text][placeholder=SearchDownloads].searchbar', {
value: searchString,
oninput: (e) => {
searchString = e.target.value.toLowerCase();
@ -232,6 +283,37 @@ function compareArrays(big, small) {
return !this.has(val);
}, new Set(small));
}
const MyFilesTable = () => {
return {
view: (v) =>
m('table.myfiles', [
m('tr', [m('th', ''), m('th', 'My Directories'), m('th', 'Size')]),
v.children,
]),
};
};
const FriendsFilesTable = () => {
return {
view: (v) =>
m('table.friendsfiles', [
m('tr', [
m('th', ''),
m('th', 'Friends Directories'),
m('th', 'Size'),
m('th', m('i.fas.fa-download')),
]),
v.children,
]),
};
};
function formatbytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
module.exports = {
RS_FILE_CTRL_PAUSE,
@ -246,8 +328,12 @@ module.exports = {
FT_STATE_PAUSED,
FT_STATE_CHECKING_HASH,
RS_FILE_REQ_ANONYMOUS_ROUTING,
RS_FILE_HINTS_REMOTE,
makeFriendlyUnit,
File,
SearchBar,
compareArrays,
MyFilesTable,
FriendsFilesTable,
formatbytes,
};

View File

@ -0,0 +1,173 @@
const m = require('mithril');
const rs = require('rswebui');
const util = require('files/files_util');
const widget = require('widgets');
const fileDown = require('files/files_downloads');
function displayfiles() {
const childrenList = []; // stores children details
let loaded = false; // checks whether we have loaded the children details or not.
let parStruct; // stores current struct(details, showChild)
let isFile = false;
let haveFile = false;
let isId = false;
let nameOfId;
return {
oninit: async (v) => {
if (v.attrs.par_directory) {
parStruct = v.attrs.par_directory;
if (Number(parStruct.details.hash) !== 0) {
isFile = true;
const res = await rs.rsJsonApiRequest('/rsfiles/alreadyHaveFile', {
// checks if the file is already there with the user
hash: parStruct.details.hash,
});
haveFile = res.body.retval;
}
}
if (v.attrs.replyDepth === 1 && parStruct) {
isId = true;
const res = await rs.rsJsonApiRequest('/rsPeers/getPeerDetails', {
sslId: parStruct.details.name,
});
if (res.body.retval) {
nameOfId = res.body.det.name;
}
}
},
view: (v) => [
m('tr', [
parStruct && Object.keys(parStruct.details.children).length
? m(
'td',
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (parStruct.showChild ? '90' : '0'),
style: 'margin-top:12px',
onclick: () => {
if (!loaded) {
// if it is not already retrieved.
parStruct.details.children.map(async (child) => {
const res = await rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {
handle: child.handle.xint64,
flags: util.RS_FILE_HINTS_REMOTE,
});
childrenList.push(res.body.details);
loaded = true;
});
}
parStruct.showChild = !parStruct.showChild;
},
})
)
: m('td', ''),
m(
'td',
{
style: {
position: 'relative',
'--replyDepth': v.attrs.replyDepth,
left: `calc(30px*${v.attrs.replyDepth})`,
},
},
isId
? nameOfId + ' (' + parStruct.details.name.slice(0, 8) + '...)'
: parStruct.details.name
),
m('td', util.formatbytes(parStruct.details.size.xint64)),
isFile &&
m(
'td',
// using the file from files_util to display download.
fileDown.list[parStruct.details.hash]
? m(util.File, {
info: fileDown.list[parStruct.details.hash],
direction: 'down',
transferred: fileDown.list[parStruct.details.hash].transfered.xint64,
parts: [],
})
: m(
'button',
{
style: { fontSize: '0.9em' },
onclick: async () => {
widget.popupMessage([
m('p', 'Start Download?'),
m(
'button',
{
onclick: async () => {
if (!haveFile) {
const res = await rs.rsJsonApiRequest('/rsFiles/FileRequest', {
fileName: parStruct.details.name,
hash: parStruct.details.hash,
flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING,
size: {
xstr64: parStruct.details.size.xstr64,
},
});
res.body.retval === false
? widget.popupMessage([
m('h3', 'Error'),
m('hr'),
m('p', res.body.errorMessage),
])
: widget.popupMessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Download Started'),
]);
m.redraw();
}
},
},
'Start Download'
),
]);
},
},
haveFile ? 'Open File' : ['Download', m('i.fas.fa-download')]
)
),
]),
parStruct.showChild && // recursive call to show children
childrenList.map((child) =>
m(displayfiles, {
par_directory: { details: child, showChild: false },
replyDepth: v.attrs.replyDepth + 1,
})
),
],
};
}
const Layout = () => {
// let root_handle;
let parent;
return {
oninit: async () => {
const res = await rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {
flags: util.RS_FILE_HINTS_REMOTE,
});
parent = res;
},
view: (v) => [
m('.widget', [
m('h3', 'Friends Files'),
m(
util.FriendsFilesTable,
m(
'tbody',
parent && // root
m(displayfiles, {
par_directory: { details: parent.body.details, showChild: false },
replyDepth: 0,
})
)
),
]),
],
};
};
module.exports = Layout;

View File

@ -0,0 +1,90 @@
const m = require('mithril');
const rs = require('rswebui');
const util = require('files/files_util');
function displayfiles() {
const childrenList = []; // stores children details
let loaded = false; // checks whether we have loaded the children details or not.
let parStruct; // stores current struct(details, showChild)
return {
oninit: (v) => {
if (v.attrs.par_directory) {
parStruct = v.attrs.par_directory;
}
},
view: (v) => [
m('tr', [
parStruct && Object.keys(parStruct.details.children).length
? m(
'td',
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (parStruct.showChild ? '90' : '0'),
style: 'margin-top:12px',
onclick: () => {
if (!loaded) { // if it is not already retrieved
parStruct.details.children.map(async (child) => {
const res = await rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {
handle: child.handle.xint64,
});
childrenList.push(res.body.details);
loaded = true;
});
}
parStruct.showChild = !parStruct.showChild;
},
})
)
: m('td', ''),
m(
'td',
{
style: {
position: 'relative',
'--replyDepth': v.attrs.replyDepth,
left: `calc(30px*${v.attrs.replyDepth})`,
},
},
parStruct.details.name
),
m('td', util.formatbytes(parStruct.details.size.xint64)),
]),
parStruct.showChild &&
childrenList.map((child) =>
m(displayfiles, { // recursive call
par_directory: { details: child, showChild: false },
replyDepth: v.attrs.replyDepth + 1,
})
),
],
};
}
const Layout = () => {
// let root_handle;
let parent;
return {
oninit: async () => {
const res = await rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {});
parent = res;
},
view: (v) => [
m('.widget', [
m('h3', 'My Files'),
m(
util.MyFilesTable,
m(
'tbody',
parent &&
m(displayfiles, {
par_directory: { details: parent.body.details, showChild: false },
replyDepth: 0,
})
)
),
]),
],
};
};
module.exports = Layout;

View File

@ -1,78 +1,405 @@
const m = require('mithril');
const rs = require('rswebui');
const util = require('forums/forums_util');
const peopleUtil = require('people/people_util');
const { updatedisplayforums } = require('./forums_util');
function displaythread() {
function createforum() {
let title;
let body;
let identity;
return {
oninit: (v) => {},
view: ({ attrs: { threadStruct, replyDepth } }) => {
const thread = threadStruct.thread;
let parMap = [];
if (util.Data.ParentThreadMap[thread.mMeta.mMsgId]) {
parMap = util.Data.ParentThreadMap[thread.mMeta.mMsgId];
oninit: (vnode) => {
if (vnode.attrs.authorId) {
identity = vnode.attrs.authorId[0];
}
return [
m('tr', [
Object.keys(parMap).length
? m(
'td',
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (threadStruct.showReplies ? '90' : '0'),
style: 'margin-top:12px',
onclick: () => {
threadStruct.showReplies = !threadStruct.showReplies;
},
})
)
: m('td', ''),
m(
'td',
{
style: {
position: 'relative',
'--replyDepth': replyDepth,
left: 'calc(30px*var(--replyDepth))',
},
onclick: () => (ThreadView.showThread = thread.mMeta.mMsgId),
},
view: (vnode) =>
m('.widget', [
m('h3', 'Create Forum'),
m('hr'),
m('input[type=text][placeholder=Title]', {
oninput: (e) => (title = e.target.value),
}),
m('label[for=tags]', 'Select identity'),
m(
'select[id=idtags]',
{
value: identity,
onchange: (e) => {
identity = vnode.attrs.authorId[e.target.selectedIndex];
},
[thread.mMeta.mMsgName]
),
m('td', ''),
m('td', rs.userList.userMap[thread.mMeta.mAuthorId]),
m(
'td',
typeof thread.mMeta.mPublishTs === 'object'
? new Date(thread.mMeta.mPublishTs.xint64 * 1000).toLocaleString()
: 'undefined'
),
]),
threadStruct.showReplies &&
},
[
vnode.attrs.authorId &&
vnode.attrs.authorId.map((o) =>
m(
'option',
{ value: o },
rs.userList.userMap[o]
? rs.userList.userMap[o].toLocaleString() + ' (' + o.slice(0, 8) + '...)'
: 'No Signature'
)
),
]
),
m('textarea[rows=5][placeholder=Description]', {
style: { width: '90%', display: 'block' },
oninput: (e) => (body = e.target.value),
value: body,
}),
m(
'button',
{
onclick: async () => {
const res = await rs.rsJsonApiRequest('/rsgxsforums/createForumV2', {
name: title,
description: body,
...(Number(identity) !== 0 && { authorId: identity }), // if id == '0', authorId is left empty
});
if (res.body.retval) {
util.updatedisplayforums(res.body.forumId);
m.redraw();
}
res.body.retval === false
? util.popupmessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: util.popupmessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Forum created successfully'),
]);
},
},
'Create'
),
]),
};
}
const EditThread = () => {
let title = '';
let body = '';
return {
oninit: (vnode) => {
title = vnode.attrs.current_title;
body = vnode.attrs.current_body;
},
view: (vnode) =>
m('.widget', [
m('h3', 'Edit Thread'),
m('hr'),
m(
'iddisplay',
{
style: { display: 'block ruby' }, // same line block ruby
},
[
'Identity: ',
m('h5[id=authid]', rs.userList.userMap[vnode.attrs.authorId].toLocaleString()),
]
),
m(
'titledisplay',
{
style: { display: 'block ruby' },
},
[
'Title: ',
m('input[type=text][placeholder=Title]', {
value: vnode.attrs.current_title,
oninput: (e) => (title = e.target.value),
}),
]
),
m('textarea[rows=5]', {
style: { width: '90%', display: 'block' },
oninput: (e) => (body = e.target.value),
value: vnode.attrs.current_body,
}),
m(
'button',
{
onclick: async () => {
const res = await rs.rsJsonApiRequest('/rsgxsforums/createPost', {
forumId: vnode.attrs.forumId,
mBody: body,
title: title,
authorId: vnode.attrs.authorId,
parentId: vnode.attrs.current_parent,
origPostId: vnode.attrs.current_msgid,
});
res.body.retval === false
? util.popupmessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: util.popupmessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Thread edited successfully'),
]);
util.updatedisplayforums(vnode.attrs.forumId);
m.redraw();
},
},
'Add'
),
]),
};
};
const AddThread = () => {
let title = '';
let body = '';
let identity;
return {
oninit: (vnode) => {
if (vnode.attrs.authorId) {
identity = vnode.attrs.authorId[0];
}
},
view: (vnode) =>
m('.widget', [
m('h3', 'Add Thread'),
m('hr'),
(vnode.attrs.parent_thread !== '') > 0
? [m('h5', 'Reply to thread: '), m('p', vnode.attrs.parent_thread)]
: '',
m('input[type=text][placeholder=Title]', {
oninput: (e) => (title = e.target.value),
}),
m('label[for=tags]', 'Select identity'),
m(
'select[id=idtags]',
{
value: identity,
onchange: (e) => {
identity = vnode.attrs.authorId[e.target.selectedIndex];
},
},
[
vnode.attrs.authorId &&
vnode.attrs.authorId.map((o) =>
m(
'option',
{ value: o },
rs.userList.userMap[o].toLocaleString() + ' (' + o.slice(0, 8) + '...)'
)
),
]
),
m('textarea[rows=5]', {
style: { width: '90%', display: 'block' },
oninput: (e) => (body = e.target.value),
value: body,
}),
m(
'button',
{
onclick: async () => {
const res =
(vnode.attrs.parent_thread !== '') > 0 // is it a reply or a new thread
? await rs.rsJsonApiRequest('/rsgxsforums/createPost', {
forumId: vnode.attrs.forumId,
mBody: body,
title: title,
authorId: identity,
parentId: vnode.attrs.parentId,
})
: await rs.rsJsonApiRequest('/rsgxsforums/createPost', {
forumId: vnode.attrs.forumId,
mBody: body,
title: title,
authorId: identity,
});
res.body.retval === false
? util.popupmessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: util.popupmessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Thread added successfully'),
]);
util.updatedisplayforums(vnode.attrs.forumId);
m.redraw();
},
},
'Add'
),
]),
};
};
function displaythread() {
// recursive function to display all the threads
let groupmessagepair;
let unread;
let editpermission = false;
return {
view: (v) => {
const thread = v.attrs.threadStruct.thread;
groupmessagepair = { first: thread.mMeta.mGroupId, second: thread.mMeta.mOrigMsgId };
let parMap = [];
if (util.Data.ParentThreadMap[thread.mMeta.mOrigMsgId]) {
parMap = util.Data.ParentThreadMap[thread.mMeta.mOrigMsgId];
}
unread = thread.mMeta.mMsgStatus === util.THREAD_UNREAD;
v.attrs.identity &&
v.attrs.identity.map((val) => {
if (val.localeCompare(thread.mMeta.mAuthorId) === 0) {
// if the author of the thread matches one of our own ids
editpermission = true;
}
});
return [
m(
'tr',
{
style: unread ? { fontWeight: 'bold' } : '',
},
[
Object.keys(parMap).length // if this thread has some replies
? m(
'td',
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (v.attrs.threadStruct.showReplies ? '90' : '0'),
style: 'margin-top:12px',
onclick: () => {
v.attrs.threadStruct.showReplies = !v.attrs.threadStruct.showReplies;
},
})
)
: m('td', ''),
m(
'td',
{
style: {
position: 'relative',
'--replyDepth': v.attrs.replyDepth,
left: 'calc(30px*var(--replyDepth))', // shifts reply by 30 px
},
onclick: async () => {
v.attrs.changeThread(thread.mMeta.mOrigMsgId);
if (unread) {
const res = await rs.rsJsonApiRequest('/rsgxsforums/markRead', {
messageId: groupmessagepair,
read: true,
});
if (res.body.retval) {
updatedisplayforums(thread.mMeta.mGroupId);
m.redraw();
}
}
},
ondblclick: () =>
(v.attrs.threadStruct.showReplies = !v.attrs.threadStruct.showReplies),
},
[
thread.mMeta.mMsgName,
m('options', { style: 'display:block' }, [
m(
'button',
{
style: 'font-size:15px',
onclick: () =>
util.popupmessage(
m(AddThread, {
parent_thread: thread.mMeta.mMsgName,
forumId: thread.mMeta.mGroupId,
authorId: v.attrs.identity,
parentId: thread.mMeta.mMsgId,
})
),
},
'Reply'
),
editpermission &&
m(
'button',
{
style: 'font-size:15px',
onclick: () =>
util.popupmessage(
m(EditThread, {
current_thread: thread.mMeta.mMsgName,
forumId: thread.mMeta.mGroupId,
current_title: thread.mMeta.mMsgName,
current_body: thread.mMsg,
authorId: thread.mMeta.mAuthorId,
current_parent: thread.mMeta.mParentId,
current_msgid: thread.mMeta.mOrigMsgId,
})
),
},
'Edit'
),
]),
]
),
m(
'td',
m(
'button',
{
style: { fontSize: '15px' },
onclick: async () => {
if (!unread) {
const res = await rs.rsJsonApiRequest('/rsgxsforums/markRead', {
messageId: groupmessagepair,
read: false,
});
if (res.body.retval) {
updatedisplayforums(thread.mMeta.mGroupId);
m.redraw();
}
}
},
},
'Mark Unread'
)
),
m('td', rs.userList.userMap[thread.mMeta.mAuthorId]),
m(
'td',
typeof thread.mMeta.mPublishTs === 'object'
? new Date(thread.mMeta.mPublishTs.xint64 * 1000).toLocaleString()
: 'undefined'
),
]
),
v.attrs.threadStruct.showReplies &&
Object.keys(parMap).map((key, index) =>
m(displaythread, {
threadStruct: util.Data.Threads[parMap[key].mGroupId][parMap[key].mMsgId],
replyDepth: replyDepth + 1,
// recursive call to all replies
threadStruct: util.Data.Threads[parMap[key].mGroupId][parMap[key].mOrigMsgId],
replyDepth: v.attrs.replyDepth + 1,
identity: v.attrs.identity,
changeThread: v.attrs.changeThread,
})
),
];
},
};
}
const ThreadView = () => {
let thread = {};
let ownId;
return {
showThread: '',
oninit: (v) => {
oninit: async (v) => {
if (
util.Data.ParentThreads[v.attrs.forumId] &&
util.Data.ParentThreads[v.attrs.forumId][v.attrs.msgId]
) {
thread = util.Data.ParentThreads[v.attrs.forumId][v.attrs.msgId];
v.state.showThread = v.attrs.msgId;
}
peopleUtil.ownIds((data) => {
ownId = data;
for (let i = 0; i < ownId.length; i++) {
if (Number(ownId[i]) === 0) {
ownId.splice(i, 1);
}
}
});
},
onupdate: (v) => console.log(v.state.showThread),
view: (v) =>
m('.widget', { key: v.attrs.msgId }, [
m(
@ -97,6 +424,11 @@ const ThreadView = () => {
m(displaythread, {
threadStruct: util.Data.Threads[v.attrs.forumId][v.attrs.msgId],
replyDepth: 0,
identity: ownId,
changeThread(newThread) {
v.state.showThread = newThread;
// For displaying the messages of the threads. We pass this into the recursive function displaythreads()
},
})
)
),
@ -106,6 +438,7 @@ const ThreadView = () => {
util.Data.Threads[v.attrs.forumId] &&
util.Data.Threads[v.attrs.forumId][v.state.showThread] &&
m('p', m.trust(util.Data.Threads[v.attrs.forumId][v.state.showThread].thread.mMsg)),
// m.trust is to render html content directly.
],
]),
};
@ -118,6 +451,7 @@ const ForumView = () => {
let createDate = {};
let lastActivity = {};
let topThreads = {};
let ownId = '';
return {
oninit: (v) => {
if (util.Data.DisplayForums[v.attrs.id]) {
@ -136,6 +470,14 @@ const ForumView = () => {
if (util.Data.ParentThreads[v.attrs.id]) {
topThreads = util.Data.ParentThreads[v.attrs.id];
}
peopleUtil.ownIds((data) => {
ownId = data;
for (let i = 0; i < ownId.length; i++) {
if (Number(ownId[i]) === 0) {
ownId.splice(i, 1);
}
}
});
},
view: (v) =>
m(
@ -198,12 +540,22 @@ const ForumView = () => {
style: 'display:' + (fsubscribed ? 'block' : 'none'),
},
m('h3', 'Threads'),
m('button',
// { onclick: () => util.popupMessage() },
[
'New Thread',
m('i.fas.fa-pencil-alt'),
]),
m(
'button',
{
onclick: () => {
util.popupmessage(
m(AddThread, {
parent_thread: '',
forumId: v.attrs.id,
authorId: ownId,
parentId: '',
})
);
},
},
['New Thread', m('i.fas.fa-pencil-alt')]
),
m('hr'),
m(
util.ThreadsTable,
@ -213,11 +565,15 @@ const ForumView = () => {
m(
'tr',
{
style:
topThreads[key].mMsgStatus === util.THREAD_UNREAD
? { fontWeight: 'bold' }
: '',
onclick: () => {
m.route.set('/forums/:tab/:mGroupId/:mMsgId', {
tab: m.route.param().tab,
mGroupId: v.attrs.id,
mMsgId: topThreads[key].mMsgId,
mMsgId: topThreads[key].mOrigMsgId,
});
},
},
@ -249,4 +605,5 @@ const ForumView = () => {
module.exports = {
ForumView,
ThreadView,
createforum,
};

View File

@ -66,3 +66,14 @@ table.threadreply td:nth-child(2) {
table.threadreply td {
word-wrap: break-word;
}
table.threadreply tr:hover {
background-color: #eef3f6;
cursor: pointer;
}
#idtags{
width: 160px;
text-align: center;
font-size: medium;
margin-left: 10px;
height: 40px;
}

View File

@ -3,6 +3,7 @@ const widget = require('widgets');
const rs = require('rswebui');
const util = require('forums/forums_util');
const viewUtil = require('forums/forum_view');
const peopleUtil = require('people/people_util');
const getForums = {
All: [],
@ -30,38 +31,64 @@ const sections = {
OtherForums: require('forums/other_forums'),
};
const Layout = {
oninit: () => {
rs.setBackgroundTask(getForums.load, 5000, () => {
// return m.route.get() === '/files/files';
});
},
view: (vnode) =>
m('.tab-page', [
m(util.SearchBar, {
list: getForums.All,
}),
m(widget.Sidebar, {
tabs: Object.keys(sections),
baseRoute: '/forums/',
}),
m(
'.forums-node-panel',
const Layout = () => {
let ownId;
Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mMsgId')
? m(viewUtil.ThreadView, {
msgId: vnode.attrs.pathInfo.mMsgId,
forumId: vnode.attrs.pathInfo.mGroupId,
})
: Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mGroupId')
? m(viewUtil.ForumView, {
id: vnode.attrs.pathInfo.mGroupId,
})
: m(sections[vnode.attrs.pathInfo.tab], {
list: getForums[vnode.attrs.pathInfo.tab],
})
),
]),
return {
oninit: () => {
rs.setBackgroundTask(getForums.load, 5000, () => {
// return m.route.get() === '/files/files';
});
peopleUtil.ownIds((data) => {
ownId = data;
for (let i = 0; i < ownId.length; i++) {
if (Number(ownId[i]) === 0) {
ownId.splice(i, 1);
}
}
ownId.unshift(0);
});
},
view: (vnode) =>
m('.tab-page', [
m(util.SearchBar, {
list: getForums.All,
}),
m(
'button',
{
style: { fontSize: '1.2em', width: '200px' },
onclick: () =>
util.popupmessage(
m(viewUtil.createforum, {
authorId: ownId,
})
),
},
'Create Forum'
),
m(widget.Sidebar, {
tabs: Object.keys(sections),
baseRoute: '/forums/',
}),
m(
'.forums-node-panel',
Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mMsgId') // thread's view
? m(viewUtil.ThreadView, {
msgId: vnode.attrs.pathInfo.mMsgId,
forumId: vnode.attrs.pathInfo.mGroupId,
})
: Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mGroupId') // Forum's view
? m(viewUtil.ForumView, {
id: vnode.attrs.pathInfo.mGroupId,
})
: m(sections[vnode.attrs.pathInfo.tab], {
list: getForums[vnode.attrs.pathInfo.tab],
})
),
]),
};
};
module.exports = {

View File

@ -7,6 +7,8 @@ const GROUP_SUBSCRIBE_SUBSCRIBED = 0x04; // means: you are subscribed to a group
const GROUP_SUBSCRIBE_NOT_SUBSCRIBED = 0x08;
const GROUP_MY_FORUM = GROUP_SUBSCRIBE_ADMIN + GROUP_SUBSCRIBE_SUBSCRIBED + GROUP_SUBSCRIBE_PUBLISH;
const THREAD_UNREAD = 0x00000003;
const Data = {
DisplayForums: {},
Threads: {},
@ -20,6 +22,7 @@ async function updatedisplayforums(keyid, details = {}) {
});
details = res.body.forumsInfo[0];
Data.DisplayForums[keyid] = {
// struct for a forum
name: details.mMeta.mGroupName,
author: details.mMeta.mAuthorId,
isSearched: true,
@ -43,19 +46,39 @@ async function updatedisplayforums(keyid, details = {}) {
msgsIds: [thread.mMsgId],
});
if (res3.body.retval) {
Data.Threads[keyid][thread.mMsgId] = { thread: res3.body.msgs[0], showReplies: false };
}
if (Data.ParentThreads[keyid] === undefined) {
Data.ParentThreads[keyid] = {};
}
if (thread.mThreadId === thread.mParentId) {
Data.ParentThreads[keyid][thread.mMsgId] = thread;
} else {
if (Data.ParentThreadMap[thread.mParentId] === undefined) {
Data.ParentThreadMap[thread.mParentId] = {};
if (
res3.body.retval &&
(Data.Threads[keyid][thread.mOrigMsgId] === undefined ||
Data.Threads[keyid][thread.mOrigMsgId].thread.mMeta.mPublishTs.xint64 <
thread.mPublishTs.xint64)
// here we get the latest edited thread for each thread by comparing the publish time
) {
Data.Threads[keyid][thread.mOrigMsgId] = { thread: res3.body.msgs[0], showReplies: false };
if (
Data.Threads[keyid][thread.mOrigMsgId] &&
Data.Threads[keyid][thread.mOrigMsgId].thread.mMeta.mMsgStatus === THREAD_UNREAD
) {
let parent = Data.Threads[keyid][thread.mOrigMsgId].thread.mMeta.mParentId;
while (Data.Threads[keyid][parent]) {
// to mark all parent threads of an inread thread
Data.Threads[keyid][parent].thread.mMeta.mMsgStatus = THREAD_UNREAD;
parent = Data.Threads[keyid][parent].thread.mMeta.mParentId;
}
}
if (Data.ParentThreads[keyid] === undefined) {
Data.ParentThreads[keyid] = {};
}
if (thread.mThreadId === thread.mParentId) {
// top level thread.
Data.ParentThreads[keyid][thread.mOrigMsgId] =
Data.Threads[keyid][thread.mOrigMsgId].thread.mMeta;
} else {
if (Data.ParentThreadMap[thread.mParentId] === undefined) {
Data.ParentThreadMap[thread.mParentId] = {};
}
Data.ParentThreadMap[thread.mParentId][thread.mOrigMsgId] = thread;
}
Data.ParentThreadMap[thread.mParentId][thread.mMsgId] = thread;
}
});
}
@ -148,6 +171,23 @@ const SearchBar = () => {
}),
};
};
function popupmessage(message) {
const container = document.getElementById('modal-container');
container.style.display = 'block';
m.render(
container,
m('.modal-content', [
m(
'button.red',
{
onclick: () => (container.style.display = 'none'),
},
m('i.fas.fa-times')
),
message,
])
);
}
module.exports = {
Data,
@ -157,9 +197,12 @@ module.exports = {
ForumTable,
ThreadsTable,
ThreadsReplyTable,
popupmessage,
updatedisplayforums,
GROUP_SUBSCRIBE_ADMIN,
GROUP_SUBSCRIBE_NOT_SUBSCRIBED,
GROUP_SUBSCRIBE_PUBLISH,
GROUP_SUBSCRIBE_SUBSCRIBED,
GROUP_MY_FORUM,
THREAD_UNREAD,
};

View File

@ -203,7 +203,7 @@ function invalidCertPrompt() {
widget.popupMessage([m('h3', 'Error'), m('hr'), m('p', 'Not a valid Retroshare certificate.')]);
}
function confirmAddPrompt(details, cert) {
function confirmAddPrompt(details, cert, long) {
widget.popupMessage([
m('i.fas.fa-user-plus'),
m('h3', 'Make friend'),
@ -214,39 +214,73 @@ function confirmAddPrompt(details, cert) {
m('li', 'Location: ' + details.location + '(' + details.id + ')'),
m('li', details.isHiddenNode ? details.hiddenNodeAddress : details.extAddr),
]),
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.'),
]);
}
}),
},
'Finish'
),
long
? m(
'button',
{
onclick: async () =>{
const res = await rs.rsJsonApiRequest('/rsPeers/loadCertificateFromString', { cert });
if (res.body.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'
)
: m(
'button',
{
onclick: async () => {
const res = await rs.rsJsonApiRequest('/rsPeers/addSslOnlyFriend', {
sslId: details.id,
pgpId: details.gpg_id,
});
if (res.body.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'
),
]);
}
function addFriendFromCert(cert) {
rs.rsJsonApiRequest('/rsPeers/loadDetailsFromStringCert', { cert }, (data) => {
if (!data.retval) {
invalidCertPrompt();
return null;
}
confirmAddPrompt(data.certDetails, cert);
});
async function addFriendFromCert(cert) {
const res = await rs.rsJsonApiRequest('/rsPeers/parseShortInvite', { invite: cert });
if (res.body.retval) {
console.log(res.body);
confirmAddPrompt(res.body.details, cert, false);
} else {
rs.rsJsonApiRequest('/rsPeers/loadDetailsFromStringCert', { cert }, (data) => {
if (!data.retval) {
invalidCertPrompt();
return null;
}
confirmAddPrompt(data.certDetails, cert, true);
});
}
}
const AddFriend = () => {

View File

@ -266,4 +266,5 @@ module.exports = {
logon,
events: eventQueue.events,
userList,
loginKey,
};