mirror of
https://github.com/RetroShare/RSNewWebUI.git
synced 2026-09-12 19:50:04 +05:00
commit
27169dcfe5
BIN
data/streaming.png
Normal file
BIN
data/streaming.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
6
webui-src/app/config/config.css
Normal file
6
webui-src/app/config/config.css
Normal file
@ -0,0 +1,6 @@
|
||||
.config-node-panel {
|
||||
position: relative;
|
||||
bottom: 250px;
|
||||
margin-left: 200px;
|
||||
animation: fadein 0.5s;
|
||||
}
|
||||
@ -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),
|
||||
]),
|
||||
]),
|
||||
};
|
||||
|
||||
@ -17,7 +17,7 @@ const Layout = {
|
||||
tabs: Object.keys(sections),
|
||||
baseRoute: '/config/',
|
||||
}),
|
||||
m('.node-panel', vnode.children),
|
||||
m('.config-node-panel', vnode.children),
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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),
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
// }
|
||||
// );
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
173
webui-src/app/files/friends_files.js
Normal file
173
webui-src/app/files/friends_files.js
Normal 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;
|
||||
90
webui-src/app/files/my_files.js
Normal file
90
webui-src/app/files/my_files.js
Normal 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;
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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 = {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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 = () => {
|
||||
|
||||
@ -266,4 +266,5 @@ module.exports = {
|
||||
logon,
|
||||
events: eventQueue.events,
|
||||
userList,
|
||||
loginKey,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user