From e06efd3b0d0b3a5af41c212677595b28ecb4ae12 Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Fri, 17 Jul 2026 23:17:34 +0200 Subject: [PATCH] Added invite button for rooms improve layout net settings Added missed config settings --- webui-src/app/chat/chat.js | 144 ++++++++++++++++++++++--- webui-src/app/config/config_network.js | 68 ++++++------ webui-src/app/config/config_people.js | 21 +++- webui-src/styles.css | 64 +++++++++++ 4 files changed, 248 insertions(+), 49 deletions(-) diff --git a/webui-src/app/chat/chat.js b/webui-src/app/chat/chat.js index dcfb8ae..9808a0e 100644 --- a/webui-src/app/chat/chat.js +++ b/webui-src/app/chat/chat.js @@ -708,6 +708,9 @@ const ChatHubState = { ownGxsIdentities: [], createRoomError: '', userSortMethod: 'name', + showInviteModal: false, + friendsList: [], + selectedFriendsToInvite: new Set(), }; // ========================= Emoji Data ========================= @@ -926,6 +929,33 @@ const PublicLobbies = { // ************************* Chat Hub Sub-Components **************************** +function loadFriendsForInvite() { + ChatHubState.friendsList = []; + rs.rsJsonApiRequest('/rsPeers/getFriendList', {}, (data) => { + if (data && data.sslIds) { + data.sslIds.forEach((sslId) => { + rs.rsJsonApiRequest('/rsPeers/getPeerDetails', { sslId }, (detData) => { + if (detData && detData.det) { + rs.rsJsonApiRequest('/rsPeers/isOnline', { sslId }, (onlineData) => { + ChatHubState.friendsList.push({ + id: sslId, + name: detData.det.name, + online: onlineData ? onlineData.retval : false + }); + // Sort online friends first, then alphabetical name + ChatHubState.friendsList.sort((a, b) => { + if (a.online !== b.online) return a.online ? -1 : 1; + return a.name.localeCompare(b.name); + }); + m.redraw(); + }); + } + }); + }); + } + }); +} + const ChatRoomHeader = () => { return { view: (vnode) => { @@ -975,21 +1005,35 @@ const ChatRoomHeader = () => { }, [m('i.fas.fa-sign-out-alt'), ' Leave Chat'] ) - : m( - 'button.red', - { - title: 'Leave Room', - onclick: () => { - ChatLobbyModel.unsubscribeChatLobby(lobbyHexId, () => { - ChatHubState.selectedRoom = null; - ChatHubState.selectedRoomId = null; - ChatHubState.selectedRoomType = null; - m.route.set('/chat'); - }); + : [ + m( + 'button', + { + title: 'Invite friends to this room', + style: 'margin-right: 0.75rem;', + onclick: () => { + ChatHubState.showInviteModal = true; + loadFriendsForInvite(); + } }, - }, - [m('i.fas.fa-sign-out-alt'), ' Leave'] - ), + [m('i.fas.fa-user-plus'), ' Invite'] + ), + m( + 'button.red', + { + title: 'Leave Room', + onclick: () => { + ChatLobbyModel.unsubscribeChatLobby(lobbyHexId, () => { + ChatHubState.selectedRoom = null; + ChatHubState.selectedRoomId = null; + ChatHubState.selectedRoomType = null; + m.route.set('/chat'); + }); + }, + }, + [m('i.fas.fa-sign-out-alt'), ' Leave'] + ) + ], ]), ]); }, @@ -2002,7 +2046,7 @@ const Layout = { ChatHubState.createRoomError && m('p.error-text', { style: 'color: #ef4444; font-size: 0.85rem; margin: 0.5rem 0 0 0;' }, ChatHubState.createRoomError), m('.modal-buttons', { style: 'display: flex; justify-content: flex-end; gap: 0.75rem; margin-top: 1rem;' }, [ - m('button.btn.blue', { + m('button', { disabled: !ChatHubState.newRoomName.trim() || !ChatHubState.newRoomIdentity, onclick: () => { const name = ChatHubState.newRoomName.trim(); @@ -2037,7 +2081,7 @@ const Layout = { }); } }, 'Create'), - m('button.btn.red', { + m('button.red', { onclick: () => { ChatHubState.showCreateRoomModal = false; ChatHubState.newRoomName = ''; @@ -2049,6 +2093,74 @@ const Layout = { ]) ]) ]), + ChatHubState.showInviteModal && m('.attach-modal-overlay', [ + m('.attach-modal', { style: 'max-width: 450px;' }, [ + m('h4', 'Invite Friends to ' + (ChatHubState.selectedRoom ? ChatHubState.selectedRoom.lobby_name : '')), + m('.friends-invite-list', { style: 'max-height: 250px; overflow-y: auto; margin-top: 1rem; border: 1px solid #e2e8f0; border-radius: 0.375rem; padding: 0.5rem;' }, [ + ChatHubState.friendsList.length === 0 + ? m('p', { style: 'text-align: center; color: #64748b; font-style: italic; margin: 1rem 0;' }, 'No friends available') + : ChatHubState.friendsList.map((friend) => { + const isChecked = ChatHubState.selectedFriendsToInvite.has(friend.id); + return m('.friend-invite-item', { + style: 'display: flex; align-items: center; justify-content: space-between; padding: 0.5rem; border-bottom: 1px solid #f1f5f9; cursor: pointer;', + onclick: () => { + if (isChecked) { + ChatHubState.selectedFriendsToInvite.delete(friend.id); + } else { + ChatHubState.selectedFriendsToInvite.add(friend.id); + } + } + }, [ + m('div', { style: 'display: flex; align-items: center; gap: 0.5rem;' }, [ + m('.status-bullet', { style: { backgroundColor: friend.online ? '#22c55e' : '#94a3b8', width: '8px', height: '8px', borderRadius: '50%', display: 'inline-block' } }), + m('span', { style: 'font-weight: 500;' }, friend.name) + ]), + m('input[type=checkbox]', { + checked: isChecked, + onclick: (e) => { + e.stopPropagation(); + if (e.target.checked) { + ChatHubState.selectedFriendsToInvite.add(friend.id); + } else { + ChatHubState.selectedFriendsToInvite.delete(friend.id); + } + } + }) + ]); + }) + ]), + m('.modal-buttons', { style: 'display: flex; justify-content: flex-end; gap: 0.75rem; margin-top: 1.5rem;' }, [ + m('button', { + disabled: ChatHubState.selectedFriendsToInvite.size === 0, + onclick: () => { + const lobbyHexId = rs.idToHex(ChatHubState.selectedRoom.lobby_id); + const invitePromises = []; + ChatHubState.selectedFriendsToInvite.forEach((friendId) => { + invitePromises.push( + new Promise((resolve) => { + rs.rsJsonApiRequest('/rsChats/invitePeerToLobby', { + lobby_id: lobbyHexId, + peer_id: friendId + }, () => resolve()); + }) + ); + }); + Promise.all(invitePromises).then(() => { + ChatHubState.showInviteModal = false; + ChatHubState.selectedFriendsToInvite.clear(); + m.redraw(); + }); + } + }, 'Invite'), + m('button.red', { + onclick: () => { + ChatHubState.showInviteModal = false; + ChatHubState.selectedFriendsToInvite.clear(); + } + }, 'Cancel') + ]) + ]) + ]), ]), m('.chat-hub-right-pane', [ diff --git a/webui-src/app/config/config_network.js b/webui-src/app/config/config_network.js index ffbb2d7..28fbdee 100644 --- a/webui-src/app/config/config_network.js +++ b/webui-src/app/config/config_network.js @@ -328,40 +328,44 @@ const SetSocksProxy = () => { }); }, view: () => - m('.proxy-server', [ + m('.proxy-server-container', [ m( - 'p', + 'p.proxy-description', 'Configure your TOR and I2P SOCKS proxy here. It will allow you to also connect to hidden nodes.' ), - Object.keys(socksProxyObj).map((proxyItem) => { - return m(`.proxy-server__${proxyItem}`, [ - m('h6', `${proxyItem.toUpperCase()} Socks Proxy: `), - m('input[type=text]', { - value: socksProxyObj[proxyItem].addr, - oninput: (e) => (socksProxyObj[proxyItem].addr = e.target.value), - onchange: () => handleProxyChange(proxyItem), - }), - m('input[type=number]', { - value: socksProxyObj[proxyItem].port, - oninput: (e) => (socksProxyObj[proxyItem].port = parseInt(e.target.value)), - onchange: () => handleProxyChange(proxyItem), - }), - socksProxyObj[proxyItem].outgoing !== undefined && - m('.proxy-outgoing', [ - m('.proxy-outgoing__status', { - style: { - backgroundColor: socksProxyObj[proxyItem].outgoing ? '#00dd44' : '#808080', - }, - }), - m( - 'p', - `${proxyItem.toUpperCase()} outgoing ${ - socksProxyObj[proxyItem].outgoing ? 'on' : 'off' - }` - ), - ]), - ]); - }), + m('.proxy-rows-container', + Object.keys(socksProxyObj).map((proxyItem) => { + const isTor = proxyItem === 'tor'; + const labelText = isTor ? 'TOR Socks Proxy:' : 'I2P Socks Proxy:'; + const outgoingText = isTor ? 'TOR outgoing' : 'I2P outgoing'; + const isOutgoing = socksProxyObj[proxyItem].outgoing; + return m('.proxy-row', [ + m('label.proxy-label', labelText), + m('input[type=text].proxy-addr-input', { + value: socksProxyObj[proxyItem].addr, + oninput: (e) => (socksProxyObj[proxyItem].addr = e.target.value), + onchange: () => handleProxyChange(proxyItem), + }), + m('input[type=number].proxy-port-input', { + value: socksProxyObj[proxyItem].port, + oninput: (e) => (socksProxyObj[proxyItem].port = parseInt(e.target.value)), + onchange: () => handleProxyChange(proxyItem), + }), + socksProxyObj[proxyItem].outgoing !== undefined && + m('.proxy-status-container', [ + m('.proxy-status-bullet', { + style: { + backgroundColor: isOutgoing ? '#22c55e' : '#808080', + }, + }), + m( + 'span.proxy-status-text', + `${outgoingText} ${isOutgoing ? 'on' : 'off'}` + ), + ]), + ]); + }) + ), ]), }; }; @@ -397,7 +401,7 @@ const Component = () => { m(displayIPAddresses, { details }), ]), m('.widget__heading', m('h3', 'Hidden Service Configuration')), - m('.widget__body', [m('.grid-2col', [m(SetSocksProxy)])]), + m('.widget__body', [m(SetSocksProxy)]), ]), ]), }; diff --git a/webui-src/app/config/config_people.js b/webui-src/app/config/config_people.js index 979c717..e3c7944 100644 --- a/webui-src/app/config/config_people.js +++ b/webui-src/app/config/config_people.js @@ -5,6 +5,7 @@ const Reputation = () => { let addFriendIdAsContacts = undefined; let usePositiveDefault = undefined; let deleteBannedAfter = undefined; + let rememberBannedAfter = undefined; let negativeThreshold = undefined; let positiveThreshold = undefined; @@ -25,6 +26,11 @@ const Reputation = () => { {}, (data) => (deleteBannedAfter = data.retval) ); + rs.rsJsonApiRequest( + '/rsreputations/rememberBannedIdThreshold', + {}, + (data) => (rememberBannedAfter = data.retval) + ); rs.rsJsonApiRequest( '/rsreputations/thresholdForRemotelyPositiveReputation', {}, @@ -95,7 +101,7 @@ const Reputation = () => { () => {} ), }), - m('p', 'Delete banned identities after(in days, 0 means indefinitely):'), + m('p', 'Delete banned identities after(0 means never):'), m('input[type=number]', { oninput: (e) => (deleteBannedAfter = parseInt(e.target.value)), value: deleteBannedAfter, @@ -108,6 +114,19 @@ const Reputation = () => { () => {} ), }), + m('p', 'Reset reputation of banned identities after (0 means never):'), + m('input[type=number]', { + oninput: (e) => (rememberBannedAfter = parseInt(e.target.value)), + value: rememberBannedAfter, + onchange: () => + rs.rsJsonApiRequest( + '/rsreputations/setRememberBannedIdThreshold', + { + days: rememberBannedAfter, + }, + () => {} + ), + }), ]), ]), ]), diff --git a/webui-src/styles.css b/webui-src/styles.css index 55503f8..cb79ce8 100644 --- a/webui-src/styles.css +++ b/webui-src/styles.css @@ -1998,3 +1998,67 @@ table.mails th.sortable-th:hover { box-shadow: 0 0 4px rgba(0, 0, 0, 0.5); } +/* Hidden Service Configuration layout overrides */ +.proxy-server-container { + width: 100%; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.proxy-description { + color: #334155; + font-size: 0.95rem; + margin-bottom: 0.5rem; +} + +.proxy-rows-container { + display: flex; + flex-direction: column; + gap: 0.75rem; + width: 100%; +} + +.proxy-row { + display: grid; + grid-template-columns: 160px 220px 220px auto; + gap: 0.75rem; + align-items: center; + width: 100%; +} + +.proxy-label { + font-size: 0.95rem; + font-weight: 500; + color: #1e293b; +} + +.proxy-addr-input { + width: 100% !important; + max-width: none !important; +} + +.proxy-port-input { + width: 100% !important; + max-width: none !important; +} + +.proxy-status-container { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.proxy-status-bullet { + width: 14px; + height: 14px; + border-radius: 50%; + display: inline-block; + border: 1px solid #475569; +} + +.proxy-status-text { + font-size: 0.95rem; + color: #1e293b; +} +