Merge branch 'master' into channels

This commit is contained in:
Sukhamjot-Singh 2022-07-22 02:30:43 +05:30 committed by GitHub
commit 4e1f9bfc31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 607 additions and 1 deletions

View File

@ -0,0 +1,102 @@
.board-node-panel {
position: relative;
bottom: 200px;
margin-left: 200px;
animation: fadein 0.5s;
}
/* subject */
table.boards th:nth-child(1) {
width: 50%;
text-align: start;
}
/* subject */
table.boards td:nth-child(1) {
text-align: start;
/* Truncate text with '...' */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
table.boards tr:hover {
background-color: #eef3f6;
cursor: pointer;
}
table.boards tr.hidden{
display: none;
}
#searchboard{
position:relative;
margin-left: 250px;
}
img.boardpic {
float: left;
height: 150px;
width:150px;
padding: 25px;
}
#boarddetails{
position: relative;
margin-left: 150px;
}
.p{
margin:0;
}
#toggleunsub{
position: relative;
background: gray;
}
#grid{
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 100px;
}
.card {
width: 150px;
height: 150px;
border: 2px solid gray;
cursor: pointer;
text-align: center;
}
.card-img{
width: 120px;
height: 120px;
}
.card-title{
position: absolute;
bottom: 0px;
}
/* subject */
table.comments th{
height: 40px;
}
table.comments th:nth-child(1) {
width: 40%;
}
table.comments td {
word-wrap: break-word;
}
table.comments td:nth-child(1){
border: 3px solid lightskyblue;
border-radius: 25px;
}
table.comments tr{
height: 40px;
}
#options{
width: 100px;
text-align: center;
font-size: medium;
margin-left: 20px;
height: 40px;
}

View File

@ -0,0 +1,142 @@
const m = require('mithril');
const rs = require('rswebui');
const util = require('boards/boards_util');
const Data = util.Data;
const peopleUtil = require('people/people_util');
const BoardView = () => {
let bname = '';
let bimage = '';
let bauthor = '';
let bsubscribed = {};
let bposts = 0;
let plist = {};
let createDate = {};
let lastActivity = {};
return {
oninit: (v) => {
if (Data.DisplayBoards[v.attrs.id]) {
bname = Data.DisplayBoards[v.attrs.id].name;
bimage = Data.DisplayBoards[v.attrs.id].image;
if (rs.userList.userMap[Data.DisplayBoards[v.attrs.id].author]) {
bauthor = rs.userList.userMap[Data.DisplayBoards[v.attrs.id].author];
} else if (Number(Data.DisplayBoards[v.attrs.id].author) === 0) {
bauthor = 'No Contact Author';
} else {
bauthor = 'Unknown';
}
bsubscribed = Data.DisplayBoards[v.attrs.id].isSubscribed;
bposts = Data.DisplayBoards[v.attrs.id].posts;
createDate = Data.DisplayBoards[v.attrs.id].created;
lastActivity = Data.DisplayBoards[v.attrs.id].activity;
}
if (Data.Posts[v.attrs.id]) {
plist = Data.Posts[v.attrs.id];
}
},
view: (v) =>
m(
'.widget',
{
key: v.attrs.id,
},
[
m(
'a[title=Back]',
{
onclick: () =>
m.route.set('/boards/:tab', {
tab: m.route.param().tab,
}),
},
m('i.fas.fa-arrow-left')
),
m('h3', bname),
m(
'button',
{
onclick: async () => {
const res = await rs.rsJsonApiRequest('/rsposted/subscribeToBoard', {
boardId: v.attrs.id,
subscribe: !bsubscribed,
});
if (res.body.retval) {
bsubscribed = !bsubscribed;
Data.DisplayBoards[v.attrs.id].isSubscribed = bsubscribed;
}
},
},
bsubscribed ? 'Subscribed' : 'Subscribe'
),
m('img.boardpic', {
src: 'data:image/png;base64,' + bimage.mData.base64,
}),
m('[id=boarddetails]', [
m('p', m('b', 'Posts: '), bposts),
m(
'p',
m('b', 'Date created: '),
typeof createDate === 'object'
? new Date(createDate.xint64 * 1000).toLocaleString()
: 'undefined'
),
m('p', m('b', 'Admin: '), bauthor),
m(
'p',
m('b', 'Last activity: '),
typeof lastActivity === 'object'
? new Date(lastActivity.xint64 * 1000).toLocaleString()
: 'undefined'
),
]),
m('hr'),
m('boarddesc', m('b', 'Description: '), Data.DisplayBoards[v.attrs.id].description),
m('hr'),
m(
'postdetails',
{
style: 'display:' + (bsubscribed ? 'block' : 'none'),
},
m('h3', 'Posts'),
m(
'[id=grid]',
Object.keys(plist).map((key, index) => [
m(
'div',
{
class: 'card',
style: 'display: ' + (plist[key].isSearched? 'block': 'none'),
onclick: () => {
m.route.set('/boards/:tab/:mGroupId/:mMsgId', {
tab: m.route.param().tab,
mGroupId: v.attrs.id,
mMsgId: key,
});
},
},
[
m('img', {
class: 'card-img',
src: 'data:image/png;base64,' + plist[key].post.mThumbnail.mData.base64,
alt: 'No Thumbnail',
}),
m('div', { class: 'card-info' }, [
m('h4', { class: 'card-title' }, plist[key].post.mMeta.mMsgName),
]),
]
),
])
)
),
]
),
};
};
module.exports = {
BoardView,
};

View File

@ -0,0 +1,80 @@
const m = require('mithril');
const widget = require('widgets');
const rs = require('rswebui');
const util = require('boards/boards_util');
const viewUtil = require('boards/board_view');
const getBoards =
{
All : [],
PopularBoards : [],
SubscribedBoards : [],
MyBoards : [],
async load(){
const res = await rs.rsJsonApiRequest('/rsPosted/getBoardsSummaries');
const data = res.body;
getBoards.All = data.groupInfo;
getBoards.PopularBoards = getBoards.All;
getBoards.SubscribedBoards = getBoards.All.filter(
(board) =>
board.mSubscribeFlags === util.GROUP_SUBSCRIBE_SUBSCRIBED
);
getBoards.MyBoards = getBoards.All.filter(
(board) => board.mSubscribeFlags === util.GROUP_MY_BOARD
);
},
};
const sections = {
MyBoards: require('boards/my_boards'),
SubscribedBoards: require('boards/subscribed_boards'),
PopularBoards: require('boards/popular_boards'),
OtherBoards: require('boards/other_boards')
};
const Layout = {
oninit : () => {
rs.setBackgroundTask(getBoards.load, 5000, () => {
});
},
// onupdate: getBoards.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',
boardId: vnode.attrs.pathInfo.mGroupId
})
: m(util.SearchBar, {
category: 'boards'
}),
m(widget.Sidebar, {
tabs: Object.keys(sections),
baseRoute: '/boards/',
}),
m('.board-node-panel',
Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mMsgId')
? m(viewUtil.PostView, {
msgId: vnode.attrs.pathInfo.mMsgId,
boardId: vnode.attrs.pathInfo.mGroupId,
})
: Object.prototype.hasOwnProperty.call(vnode.attrs.pathInfo, 'mGroupId')
? m(viewUtil.BoardView, {
id: vnode.attrs.pathInfo.mGroupId,
})
: m(sections[vnode.attrs.pathInfo.tab], {
list: getBoards[vnode.attrs.pathInfo.tab],
})
),
]),
};
module.exports = {
view: (vnode) => {
return m(Layout, {
pathInfo: vnode.attrs,
});
},
};

View File

@ -0,0 +1,159 @@
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.
const GROUP_SUBSCRIBE_NOT_SUBSCRIBED = 0x08;
const GROUP_MY_BOARD = GROUP_SUBSCRIBE_ADMIN + GROUP_SUBSCRIBE_SUBSCRIBED + GROUP_SUBSCRIBE_PUBLISH;
const GXS_VOTE_DOWN = 0x0001;
const GXS_VOTE_UP = 0x0002;
const Data = {
DisplayBoards: {},
Posts: {},
Comments: {},
};
async function updateContent(content, boardid) {
const res = await rs.rsJsonApiRequest('/rsPosted/getBoardContent', {
boardId: boardid,
contentsIds: [content.mMsgId],
});
if (res.body.retval && res.body.posts.length > 0) {
Data.Posts[boardid][content.mMsgId] = { post: res.body.posts[0], isSearched: true };
} else if (res.body.retval && res.body.comments.length > 0) {
if (Data.Comments[content.mThreadId] === undefined) {
Data.Comments[content.mThreadId] = {};
}
Data.Comments[content.mThreadId][content.mMsgId] = res.body.comments[0];
} 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]
) {
if (vote.mVoteType === GXS_VOTE_UP) {
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].mUpVotes += 1;
}
if (vote.mVoteType === GXS_VOTE_DOWN) {
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].mDownVotes += 1;
}
}
}
}
async function updateDisplayBoards(keyid, details) {
const res1 = await rs.rsJsonApiRequest('/rsPosted/getBoardsInfo', {
boardsIds: [keyid],
});
details = res1.body.boardsInfo[0];
Data.DisplayBoards[keyid] = {
name: details.mMeta.mGroupName,
isSearched: true,
description: details.mDescription,
image: details.mGroupImage,
author: details.mMeta.mAuthorId,
isSubscribed:
details.mMeta.mSubscribeFlags === GROUP_SUBSCRIBE_SUBSCRIBED ||
details.mMeta.mSubscribeFlags === GROUP_MY_BOARD,
posts: details.mMeta.mVisibleMsgCount,
activity: details.mMeta.mLastPost,
created: details.mMeta.mPublishTs,
all: details,
};
if (Data.Posts[keyid] === undefined) {
Data.Posts[keyid] = {};
}
/*const res2 = await rs.rsJsonApiRequest('/rsPosted/getContentSummaries', {
boardId: keyid,
});
if (res2.body.retval) {
res2.body.summaries.map((content) => {
updateContent(content, keyid);
});
}*/
}
const DisplayBoardsFromList = () => {
return {
oninit: (v) => {},
view: (v) =>
m(
'tr',
{
key: v.attrs.id,
class:
Data.DisplayBoards[v.attrs.id] && Data.DisplayBoards[v.attrs.id].isSearched
? ''
: 'hidden',
onclick: () => {
m.route.set('/boards/:tab/:mGroupId', {
tab: v.attrs.category,
mGroupId: v.attrs.id,
});
},
},
[m('td', Data.DisplayBoards[v.attrs.id] ? Data.DisplayBoards[v.attrs.id].name : '')]
),
};
};
const BoardSummary = () => {
let keyid = {};
return {
oninit: (v) => {
keyid = v.attrs.details.mGroupId;
updateDisplayBoards(keyid);
},
view: (v) => {},
};
};
const BoardTable = () => {
return {
oninit: (v) => {},
view: (v) => m('table.boards', [m('tr', [m('th', 'Board Name')]), v.children]),
};
};
const SearchBar = () => {
let searchString = '';
return {
view: (v) =>
m('input[type=text][id=searchboard][placeholder=Search Subject].searchbar', {
value: searchString,
oninput: (e) => {
searchString = e.target.value.toLowerCase();
for (const hash in Data.DisplayBoards) {
if (Data.DisplayBoards[hash].name.toLowerCase().indexOf(searchString) > -1) {
Data.DisplayBoards[hash].isSearched = true;
} else {
Data.DisplayBoards[hash].isSearched = false;
}
}
},
}),
};
};
module.exports = {
Data,
SearchBar,
BoardSummary,
DisplayBoardsFromList,
updateDisplayBoards,
BoardTable,
GROUP_SUBSCRIBE_ADMIN,
GROUP_SUBSCRIBE_NOT_SUBSCRIBED,
GROUP_SUBSCRIBE_PUBLISH,
GROUP_SUBSCRIBE_SUBSCRIBED,
GROUP_MY_BOARD,
GXS_VOTE_DOWN,
GXS_VOTE_UP,
};

View File

@ -0,0 +1,32 @@
const m = require('mithril');
const util = require('boards/boards_util');
const Layout = () => {
return {
view: (v) => [
m('.widget', [
m('h3', 'My Boards'),
m('hr'),
m(
util.BoardTable,
m('tbody', [
v.attrs.list.map((board) =>
m(util.BoardSummary, {
details: board,
category: 'MyBoards',
})
),
v.attrs.list.map((board) =>
m(util.DisplayBoardsFromList, {
id: board.mGroupId,
category: 'MyBoards',
})
),
])
),
]),
],
};
};
module.exports = Layout();

View File

@ -0,0 +1,11 @@
const m = require('mithril');
const Layout = () => {
return{
view: () => [
m('.widget', [m('h2', 'Other Boards'), m('hr'), ])
]
};
};
module.exports = Layout();

View File

@ -0,0 +1,33 @@
const m = require('mithril');
const util = require('boards/boards_util');
const Layout = () => {
return {
view: (v) => [
m('.widget', [
m('h3', 'Popular Boards'),
m('hr'),
m(
util.BoardTable,
m('tbody', [
v.attrs.list.map((board) =>
m(util.BoardSummary, {
details: board,
category: 'PopularBoards',
}),
),
v.attrs.list.map((board) =>
m(util.DisplayBoardsFromList, {
id: board.mGroupId,
category: 'PopularBoards',
}),
),
])
),
]),
],
};
};
module.exports = Layout;

View File

@ -0,0 +1,33 @@
const m = require('mithril');
const util = require('boards/boards_util');
const Layout = () => {
return {
view: (v) => [
m('.widget', [
m('h3', 'Subscribed Boards'),
m('hr'),
m(
util.BoardTable,
m('tbody', [
v.attrs.list.map((board) =>
m(util.BoardSummary, {
details: board,
category: 'SubscribedBoards',
}),
),
v.attrs.list.map((board) =>
m(util.DisplayBoardsFromList, {
id: board.mGroupId,
category: 'SubscribedBoards',
}),
),
])
),
]),
],
};
};
module.exports = Layout;

View File

@ -9,6 +9,7 @@ const mail = require('mail/mail_resolver');
const files = require('files/files_resolver');
const channels = require('channels/channels');
const forums = require('forums/forums');
const boards = require('boards/boards');
const config = require('config/config_resolver');
const navIcon = {
@ -20,6 +21,7 @@ const navIcon = {
files: m('i.fas.fa-folder-open.sidenav-icon'),
channels: m('i.fas.fa-tv.sidenav-icon'),
forums: m('i.fas.fa-bullhorn.sidenav-icon'),
boards: m('i.fas.fa-globe.sidenav-icon'),
config: m('i.fas.fa-cogs.sidenav-icon'),
};
@ -56,7 +58,8 @@ const Layout = () => {
mail: '/mail/inbox',
files: '/files/files',
channels: '/channels/MyChannels',
forums: 'forums/MyForums',
forums: '/forums/MyForums',
boards: '/boards/MyBoards',
config: '/config/network',
},
}),
@ -112,8 +115,19 @@ m.route(document.getElementById('main'), '/', {
'/forums/:tab/:mGroupId': {
render: (v) => m(Layout, m(forums, v.attrs)),
},
'/forums/:tab/:mGroupId/:mMsgId': {
render: (v) => m(Layout, m(forums, v.attrs)),
'/boards/:tab': {
render: (v) => m(Layout, m(boards, v.attrs)),
},
'/boards/:tab/:mGroupId': {
render: (v) => m(Layout, m(boards, v.attrs)),
},
'/boards/:tab/:mGroupId/:mMsgId': {
render: (v) => m(Layout, m(boards, v.attrs)),
},
'/config/:tab': {
render: (v) => m(Layout, m(config, v.attrs)),