diff --git a/webui-src/.eslintrc.json b/webui-src/.eslintrc.json index 8287d15..3b315cb 100644 --- a/webui-src/.eslintrc.json +++ b/webui-src/.eslintrc.json @@ -1,60 +1,57 @@ { - "root": true, - "env": { - "browser": true, - "commonjs": true, - "es6": true - }, - "extends": "eslint:recommended", - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly", - "window": "readonly", - "document": "readonly" - }, - "parserOptions": { - "ecmaVersion": 2018 - }, - "ignorePatterns": ["mithril.js", "assets/*", "make-src/*"], - "rules": { - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ], - "arrow-spacing": ["error", { "before": true, "after": true }], - "arrow-parens": ["error", "always"], - "comma-style": ["error", "last"], - "comma-spacing": ["error", { "before": false, "after": true }], - "camelcase": ["error", { - "allow": ["^UNSAFE_"], - "properties": "never", - "ignoreGlobals": true - }], - "eol-last": "error", - "eqeqeq": ["error", "always", { "null": "ignore" }], - "no-irregular-whitespace": ["error"], - "no-trailing-spaces": ["error"], - "no-unexpected-multiline": ["error"], - "no-unreachable": ["error"], - "no-var": "warn", - "no-unused-vars": ["error", { - "args": "none", - "caughtErrors": "none", - "ignoreRestSiblings": true, - "vars": "all" - }], - "no-use-before-define": ["warn", { "functions": true, "classes": false }], - "object-shorthand": "error", - "prefer-const": ["error", { "destructuring": "all" }], - "semi-style": ["warn", "last"], - "spaced-comment": ["warn", "always"] - } + "root": true, + "env": { + "browser": true, + "commonjs": true, + "es6": true + }, + "extends": "eslint:recommended", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly", + "window": "readonly", + "document": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018 + }, + "ignorePatterns": ["mithril.js", "assets/*", "make-src/*"], + "rules": { + "linebreak-style": ["off", "unix"], + "quotes": ["error", "single"], + "semi": ["error", "always"], + "arrow-spacing": ["error", { "before": true, "after": true }], + "arrow-parens": ["error", "always"], + "comma-style": ["error", "last"], + "comma-spacing": ["error", { "before": false, "after": true }], + "camelcase": [ + "error", + { + "allow": ["^UNSAFE_"], + "properties": "never", + "ignoreGlobals": true + } + ], + "eol-last": "error", + "eqeqeq": ["error", "always", { "null": "ignore" }], + "no-irregular-whitespace": ["error"], + "no-trailing-spaces": ["error"], + "no-unexpected-multiline": ["error"], + "no-unreachable": ["error"], + "no-var": "warn", + "no-unused-vars": [ + "error", + { + "args": "none", + "caughtErrors": "none", + "ignoreRestSiblings": true, + "vars": "all" + } + ], + "no-use-before-define": ["warn", { "functions": true, "classes": false }], + "object-shorthand": "error", + "prefer-const": ["error", { "destructuring": "all" }], + "semi-style": ["warn", "last"], + "spaced-comment": ["warn", "always"] + } } diff --git a/webui-src/app/chat/chat.js b/webui-src/app/chat/chat.js index ed35147..aab96e9 100644 --- a/webui-src/app/chat/chat.js +++ b/webui-src/app/chat/chat.js @@ -279,6 +279,7 @@ const Lobby = () => { ChatLobbyModel.selected(info, '.selected-lobby', tagname), { key: info.lobby_id.xstr64, + onclick, }, [ diff --git a/webui-src/app/config/config_files.js b/webui-src/app/config/config_files.js index 4511b55..dfca251 100644 --- a/webui-src/app/config/config_files.js +++ b/webui-src/app/config/config_files.js @@ -25,6 +25,7 @@ const DownloadDirectory = () => { let dlDir = ''; const setDir = () => { // const path = document.getElementById('dl-dir-input').value; // unused? + rs.rsJsonApiRequest( 'rsFiles/setDownloadDirectory', { @@ -54,6 +55,7 @@ const PartialsDirectory = () => { let partialsDir = ''; const setDir = () => { // const path = document.getElementById('partial-dir-input').value; // unused? + rs.rsJsonApiRequest( 'rsFiles/setPartialsDirectory', { diff --git a/webui-src/app/config/config_network.js b/webui-src/app/config/config_network.js index eb99bc1..9b84153 100644 --- a/webui-src/app/config/config_network.js +++ b/webui-src/app/config/config_network.js @@ -3,10 +3,13 @@ const rs = require('rswebui'); const util = require('config/config_util'); // for future use -/* eslint-disable no-unused-vars */ const SetNwMode = () => { +/* eslint-disable no-unused-vars */ + +const SetNwMode = () => { let mode = undefined; let sslId = undefined; const setMode = () => rs.rsJsonApiRequest('/'); + return { oninit: util.getSslId().then((val) => { sslId = val; @@ -123,6 +126,7 @@ const Component = () => { m('.widget.widget-half', [ m('h3', 'Network Configuration'), m('hr'), + m('.grid-2col', [ m(SetLimits), m(SetOpMode), diff --git a/webui-src/app/config/config_resolver.js b/webui-src/app/config/config_resolver.js index 3544e5e..d03f162 100644 --- a/webui-src/app/config/config_resolver.js +++ b/webui-src/app/config/config_resolver.js @@ -1,4 +1,5 @@ const m = require('mithril'); + const widget = require('widgets'); const sections = { diff --git a/webui-src/app/config/config_services.js b/webui-src/app/config/config_services.js index 5cfd7df..b6ca8e2 100644 --- a/webui-src/app/config/config_services.js +++ b/webui-src/app/config/config_services.js @@ -3,6 +3,7 @@ const rs = require('rswebui'); const servicesInfo = { list: [], + setData(data) { servicesInfo.list = data.info.mServiceList; }, diff --git a/webui-src/app/files/files_downloads.js b/webui-src/app/files/files_downloads.js index 3c941a7..7ca9bb3 100644 --- a/webui-src/app/files/files_downloads.js +++ b/webui-src/app/files/files_downloads.js @@ -87,6 +87,7 @@ function addFile(url) { { fileName: details.name, hash: details.hash, + flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING, size: Number.parseInt(details.size), }, diff --git a/webui-src/app/files/files_resolver.js b/webui-src/app/files/files_resolver.js index b8c3295..2159bb5 100644 --- a/webui-src/app/files/files_resolver.js +++ b/webui-src/app/files/files_resolver.js @@ -1,4 +1,5 @@ const m = require('mithril'); + const widget = require('widgets'); const downloads = require('files/files_downloads'); diff --git a/webui-src/app/files/files_util.js b/webui-src/app/files/files_util.js index fec21ee..e2d6b76 100644 --- a/webui-src/app/files/files_util.js +++ b/webui-src/app/files/files_util.js @@ -92,6 +92,7 @@ function actionButton(file, action) { 'button', { title: 'resume', + onclick() { fileAction(file.hash, 'resume'); }, @@ -104,6 +105,7 @@ function actionButton(file, action) { 'button', { title: 'pause', + onclick() { fileAction(file.hash, 'pause'); }, @@ -116,6 +118,7 @@ function actionButton(file, action) { 'button.red', { title: 'cancel', + onclick() { fileAction(file.hash, 'cancel'); }, diff --git a/webui-src/app/login.js b/webui-src/app/login.js index 8d902ed..a113db1 100644 --- a/webui-src/app/login.js +++ b/webui-src/app/login.js @@ -59,6 +59,7 @@ function loginComponent() { }, 'Login' ); + const inputPassword = () => m('input[autofocus]', { id: 'password', @@ -68,7 +69,9 @@ function loginComponent() { onkeydown: (e) => { if (e.keyCode === 13) { passwd = e.target.value; + buttonLogin().click(); + return false; } return true; diff --git a/webui-src/app/mail/mail_draftbox.js b/webui-src/app/mail/mail_draftbox.js index bbba56e..50d71ad 100644 --- a/webui-src/app/mail/mail_draftbox.js +++ b/webui-src/app/mail/mail_draftbox.js @@ -1,4 +1,5 @@ const m = require('mithril'); + const util = require('mail/mail_util'); const Layout = () => { diff --git a/webui-src/app/mail/mail_resolver.js b/webui-src/app/mail/mail_resolver.js index 5925456..34e3129 100644 --- a/webui-src/app/mail/mail_resolver.js +++ b/webui-src/app/mail/mail_resolver.js @@ -51,6 +51,7 @@ module.exports = { view: (v) => { const tab = v.attrs.tab; // TODO: utilize multiple routing params + if (Object.prototype.hasOwnProperty.call(v.attrs, 'msgId')) { return m( Layout, diff --git a/webui-src/app/mail/mail_sentbox.js b/webui-src/app/mail/mail_sentbox.js index f13f4d4..ae8b63e 100644 --- a/webui-src/app/mail/mail_sentbox.js +++ b/webui-src/app/mail/mail_sentbox.js @@ -1,4 +1,5 @@ const m = require('mithril'); + const util = require('mail/mail_util'); const Layout = () => { diff --git a/webui-src/app/mail/mail_util.js b/webui-src/app/mail/mail_util.js index ac01e55..f9f0843 100644 --- a/webui-src/app/mail/mail_util.js +++ b/webui-src/app/mail/mail_util.js @@ -27,7 +27,9 @@ const MessageSummary = () => { (data) => { details = data.msg; files = details.files; + isStarred = (details.msgflags & 0xf00) === RS_MSG_STAR; + const flag = details.msgflags & 0xf0; if (flag === RS_MSG_NEW || flag === RS_MSG_UNREAD_BY_USER) { msgStatus = 'unread'; @@ -78,6 +80,7 @@ const MessageSummary = () => { // m('td', details.rspeerid_srcId == 0 ? // '[Notification]' : // peopleUtils.getInfo(details.rspeerid_srcId)), // getInfo previously uses "/rsIdentity/getIdentitiesInfo" + m('td', new Date(details.ts * 1000).toLocaleString()), ] ), diff --git a/webui-src/app/main.js b/webui-src/app/main.js index b4e342c..e675f49 100644 --- a/webui-src/app/main.js +++ b/webui-src/app/main.js @@ -3,7 +3,7 @@ const m = require('mithril'); const login = require('login'); const home = require('home'); const network = require('network/network'); -const people = require('people/people'); +const people = require('people/people_resolver'); const chat = require('chat/chat'); const mail = require('mail/mail_resolver'); const files = require('files/files_resolver'); @@ -49,7 +49,7 @@ const Layout = () => { links: { home: '/home', network: '/network', - people: '/people', + people: '/people/OwnIdentity', chat: '/chat', mail: '/mail/inbox', files: '/files/files', @@ -72,8 +72,9 @@ m.route(document.getElementById('main'), '/', { '/network': { render: () => m(Layout, m(network)), }, - '/people': { - render: () => m(Layout, m(people)), + + '/people/:tab': { + render: (v) => m(Layout, m(people, v.attrs)), }, '/chat/:lobby/:subaction': { render: (v) => m(Layout, m(chat, v.attrs)), diff --git a/webui-src/app/network/network.js b/webui-src/app/network/network.js index e49c6c4..211c63c 100644 --- a/webui-src/app/network/network.js +++ b/webui-src/app/network/network.js @@ -122,11 +122,6 @@ const FriendsList = () => { return { oninit: () => { Data.refreshGpgDetails(); - // rs.setBackgroundTask( - // Data.refreshGpgDetails, - // 10000, - // () => m.route.get() === '/network', - // ) }, view: () => m('.widget', [ diff --git a/webui-src/app/network/network_data.js b/webui-src/app/network/network_data.js index bd07e0e..de6bc61 100644 --- a/webui-src/app/network/network_data.js +++ b/webui-src/app/network/network_data.js @@ -17,7 +17,10 @@ async function loadSslDetails() { return sslDetails; } -async function freshGpgDetails() { +const Data = { + gpgDetails: {}, +}; +Data.refreshGpgDetails = async function () { const details = {}; const sslDetails = await loadSslDetails(); await Promise.all( @@ -52,14 +55,6 @@ async function freshGpgDetails() { }); }) ); - return details; -} - -const Data = { - gpgDetails: {}, - refreshGpgDetails() { - Data.gpgDetails = freshGpgDetails(); - }, + Data.gpgDetails = details; }; - module.exports = Data; diff --git a/webui-src/app/people/people.js b/webui-src/app/people/people.js index 52012a9..ef071fc 100644 --- a/webui-src/app/people/people.js +++ b/webui-src/app/people/people.js @@ -1,38 +1,23 @@ const m = require('mithril'); const rs = require('rswebui'); -const peopleOwnIds = require('people/people_ownids'); const peopleUtil = require('people/people_util'); -const AllContacts = { - oninit: () => {}, - view: () => { - const list = peopleUtil.sortUsers(rs.userList.users); - return m('.widget', [ - m('h3', 'Contacts', m('span.counter', list.length)), - m('hr'), - list.map((id) => - m( - '.id', - { - key: id.mGroupId, - title: 'ID: ' + id.mGroupId, - }, - id.mGroupName, - m('i.chatInit.fa.fa-comment-medical', { - title: 'start distant chat with ' + id.mGroupName + ' (' + id.mGroupId + ')', - onclick: () => { - m.route.set('/chat/:userid/createdistantchat', { userid: id.mGroupId }); - }, - }) - ) - ), - ]); - }, +const AllContacts = () => { + const list = peopleUtil.sortUsers(rs.userList.users); + return { + view: () => { + return m('.widget', [ + m('h3', 'Contacts', m('span.counter', list.length)), + m('hr'), + list.map((id) => m(peopleUtil.regularcontactInfo, { id })), + ]); + }, + }; }; const Layout = { - view: (vnode) => m('.tab-page', [m(peopleOwnIds), m(AllContacts)]), + view: (vnode) => m('.tab-page', [m(peopleUtil.SearchBar), m(AllContacts)]), }; module.exports = { diff --git a/webui-src/app/people/people_own_contacts.js b/webui-src/app/people/people_own_contacts.js new file mode 100644 index 0000000..dcf130d --- /dev/null +++ b/webui-src/app/people/people_own_contacts.js @@ -0,0 +1,26 @@ +const m = require('mithril'); +const rs = require('rswebui'); +const peopleUtil = require('people/people_util'); + +const MyContacts = () => { + const list = peopleUtil.contactlist(rs.userList.users); + return { + view: () => { + return m('.widget', [ + m('h3', 'MyContacts', m('span.counter', list.length)), + m('hr'), + list.map((id) => m(peopleUtil.regularcontactInfo, { id })), + ]); + }, + }; +}; + +const Layout = { + view: (vnode) => m('.tab-page', [m(peopleUtil.SearchBar), m(MyContacts)]), +}; + +module.exports = { + view: (vnode) => { + return m(Layout); + }, +}; diff --git a/webui-src/app/people/people_ownids.js b/webui-src/app/people/people_ownids.js index 55b50be..c33bb2e 100644 --- a/webui-src/app/people/people_ownids.js +++ b/webui-src/app/people/people_ownids.js @@ -201,7 +201,9 @@ const EditIdentity = () => { '/rsIdentity/updateIdentity', { id: v.attrs.details.mId, + name, + // avatar: v.attrs.details.mAvatar.mData.base64, pseudonimous: true, }, @@ -255,10 +257,6 @@ const DeleteIdentity = () => { const Identity = () => { let details = {}; - let avatarURI = { - view: () => [], - }; - return { oninit: (v) => rs.rsJsonApiRequest( @@ -268,9 +266,6 @@ const Identity = () => { }, (data) => { details = data.details; - // Creating URI during fetch because `details` is uninitialized - // during view run, due to request being async. - avatarURI = peopleUtil.createAvatarURI(data.details.mAvatar); } ), view: (v) => @@ -281,7 +276,7 @@ const Identity = () => { }, [ m('h4', details.mNickname), - m(avatarURI), + m(peopleUtil.UserAvatar, { avatar: details.mAvatar }), m('.details', [ m('p', 'ID:'), m('p', details.mId), diff --git a/webui-src/app/people/people_resolver.js b/webui-src/app/people/people_resolver.js new file mode 100644 index 0000000..ef887f0 --- /dev/null +++ b/webui-src/app/people/people_resolver.js @@ -0,0 +1,26 @@ +const m = require('mithril'); +const widget = require('widgets'); + +const sections = { + OwnIdentity: require('people/people_ownids'), + MyContacts: require('people/people_own_contacts'), + All: require('people/people'), +}; + +const Layout = { + view: (vnode) => + m('.tab-page', [ + m(widget.Sidebar, { + tabs: Object.keys(sections), + baseRoute: '/people/', + }), + m('.node-panel', vnode.children), + ]), +}; + +module.exports = { + view: (vnode) => { + const tab = vnode.attrs.tab; + return m(Layout, m(sections[tab])); + }, +}; diff --git a/webui-src/app/people/people_util.js b/webui-src/app/people/people_util.js index cedfdf7..d819235 100644 --- a/webui-src/app/people/people_util.js +++ b/webui-src/app/people/people_util.js @@ -1,13 +1,44 @@ -const m = require('mithril'); const rs = require('rswebui'); +const m = require('mithril'); function checksudo(id) { return id === '0000000000000000'; } +const UserAvatar = () => ({ + view: (v) => { + const imageURI = v.attrs.avatar; + console.log(imageURI); + return imageURI === undefined || imageURI.mData.base64 === '' + ? [] + : m('img.avatar', { + src: 'data:image/png;base64,' + imageURI.mData.base64, + }); + }, +}); + +function contactlist(list) { + const result = []; + if (list !== undefined) { + list.map((id) => { + id.isSearched = true; + rs.rsJsonApiRequest('/rsIdentity/isARegularContact', { id: id.mGroupId }, (data) => { + if (data.retval) result.push(id); + console.log(data); + }); + }); + } + return result; +} + function sortUsers(list) { if (list !== undefined) { - const result = [...list]; + const result = []; + list.map((id) => { + id.isSearched = true; + result.push(id); + }); + result.sort((a, b) => a.mGroupName.localeCompare(b.mGroupName)); return result; } @@ -17,6 +48,7 @@ function sortUsers(list) { function sortIds(list) { if (list !== undefined) { const result = [...list]; + result.sort((a, b) => rs.userList.username(a).localeCompare(rs.userList.username(b))); return result; } @@ -34,24 +66,99 @@ function ownIds(consumer = (list) => {}, onlySigned = false) { } }); } +const SearchBar = () => { + let searchString = ''; -function createAvatarURI(avatar) { - return avatar.mData.base64 === '' - ? { - view: () => [], - } - : { - view: () => - m('img.avatar', { - src: 'data:image/png;base64,' + avatar.mData.base64, - }), - }; -} + return { + view: () => + m('input.searchbar', { + type: 'text', + placeholder: 'search', + value: searchString, + oninput: (e) => { + searchString = e.target.value.toLowerCase(); + + rs.userList.users.map((id) => { + if (id.mGroupName.toLowerCase().indexOf(searchString) > -1) { + id.isSearched = true; + } else { + id.isSearched = false; + } + }); + }, + }), + }; +}; + +const regularcontactInfo = () => { + let details = {}; + + return { + oninit: (v) => + rs.rsJsonApiRequest( + '/rsIdentity/getIdDetails', + { + id: v.attrs.id.mGroupId, + }, + (data) => { + details = data.details; + } + ), + view: (v) => + m( + '.identity', + { + key: details.mId, + style: 'display:' + (v.attrs.id.isSearched ? 'block' : 'none'), + }, + [ + m('h4', details.mNickname), + m(UserAvatar, { avatar: details.mAvatar }), + m('.details', [ + m('p', 'ID:'), + m('p', details.mId), + m('p', 'Type:'), + m('p', details.mFlags === 14 ? 'Signed ID' : 'Anonymous ID'), + m('p', 'Owner node ID:'), + m('p', details.mPgpId), + m('p', 'Created on:'), + m( + 'p', + typeof details.mPublishTS === 'object' + ? new Date(details.mPublishTS.xint64 * 1000).toLocaleString() + : 'undefiend' + ), + m('p', 'Last used:'), + m( + 'p', + typeof details.mLastUsageTS === 'object' + ? new Date(details.mLastUsageTS.xint64 * 1000).toLocaleDateString() + : 'undefiend' + ), + ]), + m( + 'button', + { + onclick: () => + m.route.set('/chat/:userid/createdistantchat', { + userid: v.attrs.id.mGroupId, + }), + }, + 'Chat' + ), + m('button.red', {}, 'Mail'), + ] + ), + }; +}; module.exports = { sortUsers, sortIds, ownIds, - createAvatarURI, checksudo, + UserAvatar, + contactlist, + SearchBar, + regularcontactInfo, }; diff --git a/webui-src/app/rswebui.js b/webui-src/app/rswebui.js index d4d0b11..773b297 100644 --- a/webui-src/app/rswebui.js +++ b/webui-src/app/rswebui.js @@ -1,3 +1,4 @@ + const m = require('mithril'); const API_URL = 'http://127.0.0.1:9092'; @@ -50,6 +51,7 @@ function rsJsonApiRequest( serialize: handleSerialize, headers, body: data, + config, }) .then((result) => { @@ -107,6 +109,7 @@ const eventQueue = { types: { // #define RS_CHAT_TYPE_PUBLIC 1 // #define RS_CHAT_TYPE_PRIVATE 2 + 2: (chatId) => chatId.distant_chat_id, // distant chat (initiate? -> todo accept) // #define RS_CHAT_TYPE_LOBBY 3 3: (chatId) => chatId.lobby_id.xstr64, // lobby_id @@ -120,6 +123,7 @@ const eventQueue = { computeIfMissing( computeIfMissing(owner.messages, chatId.type), keyfn(chatId), + () => [] ) )