webui: restore responsive design, fix chat APIs, implement optimistic messaging, and resolve Sass build/visibility issues

This commit is contained in:
jolavillette 2026-03-13 12:29:07 +01:00
parent 71d79ab2ed
commit 7118e3b816
12 changed files with 483 additions and 86 deletions

View File

@ -225,9 +225,22 @@ const ChatLobbyModel = {
newMsgs.forEach((msg) => {
const key = this.getMessageKey(msg);
if (!this.messageKeys.has(key)) {
this.messageKeys.add(key);
this.messages.push(m(Message, msg));
added = true;
// Near-duplicate check for messages without IDs (live events vs optimistic echo)
const text = msg.msg || msg.message || '';
const isNearDuplicate = this.messages.some((existingMsg) => {
const eAttrs = existingMsg.attrs;
const eText = eAttrs.msg || eAttrs.message || '';
return (
eText === text &&
Math.abs(eAttrs.sendTime - msg.sendTime) < 5 // 5 seconds window
);
});
if (!isNearDuplicate) {
this.messageKeys.add(key);
this.messages.push(m(Message, msg));
added = true;
}
}
});
@ -275,19 +288,22 @@ const ChatLobbyModel = {
setIdentity(lobbyId, nick) {
rs.rsJsonApiRequest(
'/rsChats/setIdentityForChatLobby',
{},
{
lobby_id: { xstr64: lobbyId },
nick: nick,
},
() => m.route.set('/chat/:lobby', { lobby: lobbyId }),
true,
{},
JSON.parse,
() => '{"lobby_id":' + lobbyId + ',"nick":"' + nick + '"}'
true
);
},
enterPublicLobby(lobbyId, nick) {
// Set lobby nickname
rs.rsJsonApiRequest(
'/rsChats/joinVisibleChatLobby',
{},
{
lobby_id: { xstr64: lobbyId },
own_id: nick,
},
() => {
loadLobbyDetails(lobbyId, (info) => {
ChatRoomsModel.subscribedRooms[lobbyId] = info;
@ -303,16 +319,15 @@ const ChatLobbyModel = {
// Unsubscribe
rs.rsJsonApiRequest(
'/rsChats/unsubscribeChatLobby',
{},
{
lobby_id: { xstr64: lobbyId },
},
(data, success) => {
if (success) {
ChatRoomsModel.loadSubscribedRooms(follow);
}
},
true,
{},
JSON.parse,
() => '{"lobby_id":' + lobbyId + '}'
true
);
},
chatId() {
@ -425,6 +440,7 @@ const ChatLobbyModel = {
},
sendMessage(msg, onsuccess) {
const cid = this.chatId();
// Optimistic echo for immediate feedback
const echoMsg = {
chat_id: cid,
msg: msg,
@ -435,21 +451,12 @@ const ChatLobbyModel = {
rs.rsJsonApiRequest(
'/rsChats/sendChat',
{},
{
id: cid,
msg: msg,
},
(data, success) => {
if (success) {
// adding own message to log
rs.events[15].handler(
{
mChatMessage: {
chat_id: this.chatId(),
msg,
sendTime: new Date().getTime() / 1000,
lobby_peer_gxs_id: this.currentLobby.gxs_id,
},
},
rs.events[15]
);
onsuccess();
}
}

View File

@ -61,7 +61,7 @@ const navbar = () => {
},
title: rs.connectionState.status ? 'Connected to RetroShare Core' : 'Connection Lost',
}),
m('span.webui-version', { style: { fontSize: '0.7em', marginTop: '3px', color: '#888' } }, 'v108'),
m('span.webui-version', { style: { fontSize: '0.7em', marginTop: '3px', color: '#888' } }, 'v117'),
m('i.fas.fa-sync-alt.refresh-icon', {
style: { fontSize: '0.8em', marginTop: '2px', cursor: 'pointer', color: '#888' },
onclick: () => window.location.reload(true),

View File

@ -1,6 +1,7 @@
// -----------------------------------------------------------------------------
// This file contains all application-wide Sass mixins.
// -----------------------------------------------------------------------------
@use 'sass:color';
@use './colors' as *;
/// Button Mixin
@ -38,13 +39,17 @@
position: relative;
line-height: 1.2;
@if ($type == 'info') {
color: transparentize($dark-color, 0.2);
border: 1px solid transparentize($primary-retro-color, 0.2);
} @else if ($type == 'warning') {
border: 1px solid transparentize($golden-yellow-color, 0.2);
} @else if ($type == 'danger') {
border: 1px solid transparentize($red-color, 0.2);
@if ($type =='info') {
color: color.adjust($dark-color, $alpha: -0.2);
border: 1px solid color.adjust($primary-retro-color, $alpha: -0.2);
}
@else if ($type =='warning') {
border: 1px solid color.adjust($golden-yellow-color, $alpha: -0.2);
}
@else if ($type =='danger') {
border: 1px solid color.adjust($red-color, $alpha: -0.2);
}
&::before {
@ -53,13 +58,17 @@
top: 0.5rem;
left: 0.5rem;
@if ($type == 'info') {
@if ($type =='info') {
content: '\f05a';
color: $primary-color;
} @else if ($type == 'warning') {
}
@else if ($type =='warning') {
content: '\f071';
color: $golden-yellow-color;
} @else if ($type == 'danger') {
}
@else if ($type =='danger') {
content: '\f05a';
color: $red-color;
}
@ -69,15 +78,15 @@
@mixin flex($direction: '', $justify: '', $align: '', $gap: 0) {
display: flex;
@if ($direction != '') {
@if ($direction !='') {
flex-direction: $direction;
}
@if ($justify != '') {
@if ($justify !='') {
justify-content: $justify;
}
@if ($align != '') {
@if ($align !='') {
align-items: $align;
}
@ -88,8 +97,7 @@
// Font
@mixin fontdef-woff($FontPath, $FontName, $FontVersion: '1.0.0', $FontType: 'Regular') {
src:
url('#{$FontPath}/#{$FontName}-#{$FontType}.woff2') format('woff2'),
url('#{$FontPath}/#{$FontName}-#{$FontType}.woff') format('woff'),
url('#{$FontPath}/#{$FontName}-#{$FontType}.ttf') format('truetype');
}
src: url('#{$FontPath}/#{$FontName}-#{$FontType}.woff2') format('woff2'),
url('#{$FontPath}/#{$FontName}-#{$FontType}.woff') format('woff'),
url('#{$FontPath}/#{$FontName}-#{$FontType}.ttf') format('truetype');
}

View File

@ -1,20 +1,24 @@
@use 'sass:color';
@use '../abstracts' as *;
.media-item {
display: flex;
margin-top: 0.5rem;
padding: 1rem;
border: 1px solid transparentize($dark-color, 0.9);
border: 1px solid color.adjust($dark-color, $alpha: -0.9);
border-radius: 4px;
&__details {
flex-basis: 40%;
@include flex($align: start, $gap: 0.5rem);
& img {
width: 6rem;
object-fit: contain;
}
}
&__desc {
flex-basis: 60%;
}
}
}

View File

@ -1,5 +1,10 @@
@use 'sass:color';
@use '../abstracts' as *;
.active-link {
background: rgba(255, 255, 255, 0.1) !important;
}
/* Navbar */
.nav-menu {
background-color: $dark-color;
@ -41,7 +46,7 @@
transition: 0ms;
&:hover {
background-color: transparentize($light-color, 0.85);
background-color: color.adjust($light-color, $alpha: -0.85);
}
i.sidenav-icon {
@ -54,7 +59,7 @@
.item.item-selected {
color: $primary-light-color;
background-color: transparentize($primary-light-color, 0.85);
background-color: color.adjust($primary-light-color, $alpha: -0.85);
font-weight: medium;
}
@ -64,7 +69,7 @@
padding: 0;
top: 0;
right: -1rem;
background: lighten($primary-color, 15%);
background: color.adjust($primary-color, $lightness: 15%);
width: 1.5rem;
height: 1.5rem;
aspect-ratio: 1;
@ -77,8 +82,16 @@
&.collapsed {
.nav-menu__logo {
& h5 {
display: none;
.logo-container {
@include flex(column, $align: center, $gap: 0.5rem);
&>*:not(img) {
display: block;
}
}
& .nav-menu__logo-text {
display: none !important;
}
}
@ -89,8 +102,9 @@
justify-content: center;
transition: 300ms;
& span,
& p {
display: none;
display: none !important;
}
}
}
@ -132,7 +146,7 @@
}
.sidebarquickview {
& > h6 {
&>h6 {
padding: 0.5rem;
}
@ -172,3 +186,43 @@
border-left: 5px solid #3ba4d7;
}
}
/* GLOBAL SIDEBAR TO TABS */
@media (max-width: 700px) {
.tab-content {
flex-direction: column;
}
.sidebar {
width: 100% !important;
flex-direction: row !important;
overflow-x: auto !important;
overflow-y: hidden !important;
white-space: nowrap !important;
border-bottom: 1px solid rgba(20, 20, 27, 0.1) !important;
background: white !important;
z-index: 50 !important;
flex-shrink: 0 !important;
height: auto !important;
padding: 0 !important;
}
.sidebar a {
display: inline-block !important;
padding: 0.8rem 1.2rem !important;
border-bottom: 3px solid transparent !important;
border-left: none !important;
}
.sidebar .selected-sidebar-link {
border-left: none !important;
border-bottom: 3px solid #3ba4d7 !important;
animation: none !important;
}
/* Hide quickview headings on mobile to save space if needed */
.sidebarquickview>h4,
.sidebarquickview>h6 {
display: none !important;
}
}

View File

@ -1,3 +1,4 @@
@use 'sass:color';
@use '../abstracts' as *;
.posts {
@ -5,29 +6,34 @@
margin-top: 1rem;
flex-direction: column;
overflow: auto;
&__heading {
@include flex(column, $justify: space-between);
}
&-container {
height: 100%;
padding: 1rem;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 2rem;
border: 1px solid transparentize($dark-color, 0.9);
border: 1px solid color.adjust($dark-color, $alpha: -0.9);
border-radius: 4px;
overflow: auto;
&-card {
min-height: 240px;
flex-direction: column;
border: 1px solid transparentize($dark-color, 0.5);
border: 1px solid color.adjust($dark-color, $alpha: -0.5);
border-radius: 4px;
cursor: pointer;
text-align: center;
& img {
flex-basis: 90%;
object-fit: cover;
}
& p {
padding: 0 0.125rem;
flex-basis: 10%;
@ -37,4 +43,4 @@
}
}
}
}
}

View File

@ -1,3 +1,4 @@
@use 'sass:color';
@use '../abstracts/colors' as *;
.progress-bar {
@ -8,6 +9,7 @@
background-color: $light-color;
border-radius: 20px;
overflow: hidden;
&__status {
position: absolute;
top: 0;
@ -16,6 +18,7 @@
color: $dark-color;
background-color: $primary-color;
}
&__percent {
position: absolute;
inset: 0;
@ -23,6 +26,7 @@
width: fit-content;
height: fit-content;
}
&-chunks {
position: relative;
margin-top: 0.5rem;
@ -32,21 +36,27 @@
border-radius: 0.25rem;
overflow: hidden;
background-color: $light-color;
& .chunk {
width: 100%;
&[data-chunkVal='0'] {
background-color: transparentize($primary-light-color, 0.8);
background-color: color.adjust($primary-light-color, $alpha: -0.8);
}
&[data-chunkVal='1'] {
background-color: $red-color;
}
&[data-chunkVal='2'] {
background-color: $primary-color;
}
&[data-chunkVal='3'] {
background-color: $golden-yellow-color;
}
}
&__percent {
position: absolute;
inset: 0;
@ -55,4 +65,4 @@
height: fit-content;
}
}
}
}

View File

@ -1,3 +1,5 @@
@use '../abstracts' as *;
.lobby {
margin: 10px;
border: 1px solid #aaa;
@ -170,4 +172,175 @@ textarea.chatMsg {
.setup {
left: 165px;
}
}
/* CHAT ROOM (Single Chat) - Desktop Grid Layout */
@media (min-width: 900px) {
.node-panel.chat-room {
display: grid !important;
grid-template-columns: 250px 1fr 200px !important;
/* Lobbies, Chat, Users */
grid-template-rows: auto 1fr auto !important;
/* Header, Messages, Input */
grid-template-areas:
"lobbies header rightbar"
"lobbies messages rightbar"
"lobbies input rightbar" !important;
padding: 0 !important;
height: 100% !important;
}
.node-panel.chat-room .lobbyName {
grid-area: header;
padding: 10px;
border-bottom: 1px solid #eee;
margin: 0;
z-index: 10;
background: white;
}
.node-panel.chat-room .lobbies {
grid-area: lobbies;
position: static !important;
width: auto !important;
height: auto !important;
border-right: 1px solid #ccc;
overflow-y: auto;
display: block !important;
top: auto !important;
bottom: auto !important;
left: auto !important;
}
.node-panel.chat-room .messages {
grid-area: messages;
position: static !important;
width: auto !important;
height: auto !important;
overflow-y: auto;
padding: 10px;
left: auto !important;
right: auto !important;
top: auto !important;
bottom: auto !important;
margin: 0 !important;
}
.node-panel.chat-room .rightbar {
grid-area: rightbar;
position: static !important;
width: auto !important;
border-left: 1px solid #ccc;
overflow-y: auto;
display: block !important;
}
.node-panel.chat-room .chatMessage {
grid-area: input;
position: static !important;
width: auto !important;
height: auto !important;
border-top: 1px solid #eee;
left: auto !important;
right: auto !important;
bottom: auto !important;
flex: 0 0 auto;
padding: 10px !important;
background: white;
z-index: 10;
}
}
/* Mobile Overrides - Ensure Flex Column */
@media (max-width: 899px) {
.node-panel.chat-room {
display: flex !important;
flex-direction: column !important;
height: 100% !important;
position: relative !important;
}
.node-panel.chat-room .lobbyName {
flex: 0 0 auto;
}
.node-panel.chat-room .messages {
flex: 1 !important;
overflow-y: auto !important;
position: relative !important;
top: 0 !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
width: 100% !important;
height: auto !important;
margin: 0 !important;
}
.node-panel.chat-room .chatMessage {
flex: 0 0 auto !important;
position: relative !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
width: 100% !important;
height: auto !important;
z-index: 100;
}
.node-panel.chat-room .rightbar,
.node-panel.chat-room .lobbies {
display: none !important;
position: fixed !important;
top: 60px !important;
bottom: 0 !important;
width: 80% !important;
background: white !important;
z-index: 200 !important;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.2) !important;
}
.node-panel.chat-room.show-lobbies .lobbies {
display: block !important;
left: 0 !important;
}
.node-panel.chat-room.show-users .rightbar {
display: block !important;
right: 0 !important;
}
.chat-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 150;
}
.show-lobbies .chat-overlay,
.show-users .chat-overlay {
display: block;
}
/* Mobile Icons in Header */
.mobile-menu-icons {
display: flex;
gap: 15px;
font-size: 1.2rem;
}
.mobile-menu-icons i {
cursor: pointer;
padding: 5px;
}
}
@media (min-width: 900px) {
.mobile-menu-icons {
display: none;
}
}

View File

@ -279,3 +279,117 @@ table.friendsfiles td:nth-child(2) {
}
@include flex($justify: space-between);
}
/* FILES MODULE RESPONSIVENESS */
@media (max-width: 700px) {
/* General Files Layout */
.file-view__body-details {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
/* File Detail Grid (Downloads/Uploads) */
.file-view__body-details-stat {
grid-template-columns: 1fr;
gap: 0.5rem;
}
.file-view__body-details-stat span {
display: flex;
align-items: center;
}
/* Share Manager Table -> Cards */
.share-manager__table,
.share-manager__table thead,
.share-manager__table tbody,
.share-manager__table tr,
.share-manager__table td {
display: block;
width: 100% !important;
}
.share-manager__table thead {
display: none;
/* Hide header on mobile */
}
.share-manager__table tr {
border: 1px solid #ccc;
border-radius: 8px;
margin-bottom: 1rem;
padding: 0.5rem;
background: white;
}
.share-manager__table td {
margin-bottom: 0.5rem;
border: none !important;
padding-left: 0 !important;
}
/* My Files / Friends Files Tables -> Cards */
table.myfiles,
table.myfiles tr,
table.myfiles td,
table.friendsfiles,
table.friendsfiles tr,
table.friendsfiles td {
display: block;
width: 100% !important;
}
table.myfiles th,
table.friendsfiles th {
display: none;
}
table.myfiles tr,
table.friendsfiles tr {
border: 1px solid #ccc;
border-radius: 8px;
margin-bottom: 1rem;
padding: 0.5rem;
background: white;
}
/* Search Container Layout */
.file-search-container {
flex-direction: column;
}
.file-search-container__keywords {
flex-basis: auto;
width: 100%;
border-right: none;
border-bottom: 1px solid rgba(20, 20, 27, 0.1);
padding-bottom: 1rem;
margin-bottom: 1rem;
}
/* Search results table -> cards */
.results-container,
.results-container thead,
.results-container tbody,
.results-container tr,
.results-container td {
display: block;
width: 100% !important;
}
.results-container thead {
display: none;
}
.results-container tr {
border-bottom: 1px solid #eee;
padding: 1rem 0;
}
.results-container td {
margin-bottom: 0.5rem;
word-break: break-all;
}
}

View File

@ -1,3 +1,4 @@
@use 'sass:color';
@use '../abstracts' as *;
.homepage {
@ -6,20 +7,25 @@
.logo {
@include flex($justify: center, $align: center);
& img {
width: 90px;
}
.retroshareText {
@include flex(column, $align: center);
& .retrotext {
font-size: 36px;
font-weight: 600;
line-height: 1.125;
& > span {
&>span {
color: $primary-retro-color;
}
}
& > b {
&>b {
font-size: 14px;
line-height: 1;
}
@ -28,30 +34,36 @@
.certificate {
@include flex(column, $gap: 4rem);
&__heading {
text-align: center;
& > h1 {
&>h1 {
margin-bottom: 1rem;
}
}
&__content {
@include flex(column, $gap: 2rem);
padding: 2rem;
text-align: center;
border: 1.5px solid transparentize($primary-retro-color, 0.8);
border: 1.5px solid color.adjust($primary-retro-color, $alpha: -0.8);
border-radius: 6px;
box-shadow: 0px 0px 8px 2px transparentize($dark-color, 0.95);
.rsId > p {
box-shadow: 0px 0px 8px 2px color.adjust($dark-color, $alpha: -0.95);
.rsId>p {
margin-bottom: 0.5rem;
color: $primary-retro-color;
}
.retroshareID {
padding: 0.25rem;
@include flex($align: center);
justify-self: start;
font-size: 1.25rem;
border-radius: 4px;
background: transparentize($dark-color, 0.95);
background: color.adjust($dark-color, $alpha: -0.95);
& .textArea {
padding: 0;
width: 100%;
@ -62,10 +74,12 @@
border: none;
resize: none;
}
& i {
color: $primary-retro-color;
}
& > i {
&>i {
margin: 0 0.5rem;
cursor: pointer;
}
@ -76,27 +90,31 @@
background: whitesmoke;
@include flex($justify: center, $align: center, $gap: 0.5rem);
border-radius: 4px;
border: 1px solid transparentize($dark-color, 0.5);
border: 1px solid color.adjust($dark-color, $alpha: -0.5);
width: fit-content;
cursor: pointer;
&-container {
display: grid;
place-items: center;
}
&:hover {
background: $light-color;
border: 1px solid $dark-color;
}
& > i {
&>i {
font-size: 1.2rem;
color: green;
}
}
.add-friend > h6,
.webhelp-container > h6 {
.add-friend>h6,
.webhelp-container>h6 {
font-weight: normal;
margin-bottom: 0.5rem;
}
}
}
}
}

View File

@ -1,16 +1,16 @@
@use 'sass:color';
@use '../abstracts' as *;
.login-page {
background-image: linear-gradient(
-45deg,
transparentize($primary-color, 0.25),
transparentize($primary-retro-color, 0.25)
);
background-image: linear-gradient(-45deg,
color.adjust($primary-color, $alpha: -0.25),
color.adjust($primary-retro-color, $alpha: -0.25));
height: 100%;
animation: fadein 0.5s;
.login-container {
background-color: white;
box-shadow: 3px 3px 5px transparentize($dark-color, 0.6);
box-shadow: 3px 3px 5px color.adjust($dark-color, $alpha: -0.6);
margin: auto;
position: relative;
top: 100px;
@ -27,21 +27,24 @@
& * {
margin-bottom: 1rem;
}
& > img {
&>img {
margin: 1rem 0 2rem;
}
& extra {
margin: 0;
}
& > a {
&>a {
text-decoration: underline;
cursor: pointer;
}
}
.extra > label,
.extra > br,
.extra > input {
.extra>label,
.extra>br,
.extra>input {
margin-bottom: 0;
}
}
}

File diff suppressed because one or more lines are too long