Added invite button for rooms

improve layout net settings
Added missed config settings
This commit is contained in:
defnax 2026-07-17 23:17:34 +02:00
parent 9cb3b9bb4c
commit e06efd3b0d
4 changed files with 248 additions and 49 deletions

View File

@ -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', [

View File

@ -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)]),
]),
]),
};

View File

@ -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,
},
() => {}
),
}),
]),
]),
]),

View File

@ -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;
}