Dark mode - #126
Open
defnax wants to merge 145 commits into
Open
Conversation
Added Board posts display Added for Network to list last Chats
Fixed css issues
* corrected some place holder text
Added Navbar for webui on phones for Boards and Channels: * Validate API responses before processing. * Keep rendering/navigation functional if a response is incomplete. * Log a clear warning instead of throwing an uncaught error. * Use independent sorted arrays, avoiding mutation of the master list.
Fixed channels thumbnail layout issue Added same comments design from boards to channels
Fixed layout issues for myfiles, friendfiles & search
Brings the branch up to date with master so RetroShare#121 can be merged again. Eight files needed a decision. The branch predates 41111cb ("restore WebUI styles in SCSS sources"), which recovered into the SCSS a number of rules that until then existed only in the compiled styles.css, so most conflicts are that recovery meeting the changes made here. Where both sides describe the same element the branch's version is kept; where master's rule is a positioning context the branch has no equivalent for, master's is kept: * _chat.scss - master's attach-modal, emoji-picker, rightbar context menu and create-lobby-button rules are kept alongside the compact-message styles added here. Taking either side whole would have dropped about 450 lines. .chat-own-profile-card, .chat-hub-rightbar and .chat-hub-rightbar .user keep `position: relative`, because .chat-create-lobby-btn and .rightbar-context-menu are still declared `position: absolute` further down and would otherwise escape to the initial containing block. For .user-tooltip the branch's `position: fixed` rework wins, and master's `.chat-hub-rightbar .user-tooltip { left: -275px }` offset is dropped since it would fight a fixed element. * _people.scss - master's restored .friends-list-container context menu is kept; the pane rules it also restored are shadowed by the branch's own !important versions and were dropped as dead code. * boards_util.js - the revived updateContent() and the in-flight dedup in updateDisplayBoards() are kept. Master's side of the first two hunks was comment reformatting only. * board_view.js - the rewrite here is kept whole. Master's only change since the fork point was `let reader` -> `const reader`, in code the rewrite replaced. * chat.js - the modals moved out one nesting level here; that structure is kept. * chat_state.js - `require('people/people_util')` is not restored. It is unused, which is why 35c5c17 removed it. * build.sh - master's version. The change here concatenated raw .scss onto the generated CSS; see the following commits. * styles.css - regenerated, see the following commits.
The `@media (max-width: 768px)` block opened for the People page was never
closed, so `sass` refused the whole stylesheet:
Error: expected "}".
app/scss/pages/_people.scss 427:2 @forward
This is why the styles kept "getting destroyed": `npm run build` could not
produce styles.css at all, and the file had to be maintained by hand.
The closing brace goes before `.people-context-menu`, not at the end of the
file: that rule is written at column 0 under its own header comment, so it
is meant to be a top level rule. Left inside the media query it would only
style the right-click menu below 768px, which is where it is least likely
to be used.
styles.css is regenerated by `npm run build`, which is why it comes back
from 25200 hand-assembled lines to the 7 compressed lines the sass pipeline
emits.
build.ps1 (and the matching hunk of build.sh, dropped while rebasing)
worked around the broken stylesheet by concatenating raw .scss sources onto
the end of the generated styles.css:
$extraCss += Get-Content "$src\app\scss\pages\_board.scss"
That ships SCSS to the browser. Nesting, `&`, `@use` and variables are not
CSS, so everything after the first nested block is dropped by the parser,
which made the result look randomly broken and fed the same manual fixing
loop.
With the missing brace fixed in the previous commit, `sass` compiles the
whole tree again and the workaround has nothing left to work around.
build.ps1 was also unreachable: make-src/build.js dispatches win32 to
build.bat, never to a PowerShell script.
master gained an eslint config in 35c5c17 and the code added here predates it, so `npm run lint` fails on this branch. This commit is the mechanical part only, produced by `eslint app --fix` with no hand editing: object-shorthand, quote style, trailing whitespace, prefer-const. Twenty-two problems are left because they need a decision rather than a rewrite rule, the two worth looking at first being real: boards/board_view.js:499,503 'forumId' is not defined (no-undef) `renderComment` calls `util.voteForPost(forumId, ...)` for the up and down vote buttons, but no `forumId` exists in that scope - it looks like it came over from the forums code. Voting on a board comment therefore throws ReferenceError and silently does nothing. The other twenty are unused bindings left behind by the refactors (`get64Num`, `loadLobbyDetails`, `Message`, `SubscribedLobbies`, `PublicLobbies`, `LayoutSingle` in chat.js, `closePopup` in board_kanban.js, `bsubscribed`/`bposts`/`createDate`/`lastActivity` in boards_util.js, `displaycomment` in channel_view.js) plus a few dead assignments. `npm run lint` lists them all. Drop this commit if you would rather keep the diff to your own changes.
Merge master into improvements_v2, and fix the scss that breaks the build
trying to restore tooltip issue on chat rooms
Improved idenity selector on boards/channels loading
… item
Opening the channels list made the web UI unusable and left channel pages
blank, with the console filling up with
getChannelContent net::ERR_INSUFFICIENT_RESOURCES
[RS] Retroshare-jsonapi not available.
The second line is misleading: it is what rswebui prints for status 0, i.e.
a request that never left the browser. The service is fine, the tab is out
of sockets.
updatedisplaychannels() asked getContentSummaries for the ids of everything
in a channel - posts, comments *and* votes, each one a separate GXS item -
then fired one getChannelContent per id:
res2.body.summaries.map(async (content) => {
await updatecontent(content, keyid); // contentsIds: [content.mMsgId]
});
`.map` with an async callback awaits nothing, so all of them start at once.
And ChannelSummary calls updatedisplaychannels from its oninit, once per
channel of the list, so the whole subscription set does this simultaneously.
A few dozen posts is enough: it is the number of items across every
subscribed channel that counts, and each one costs a socket.
getChannelContent already takes a set:
rsgxschannels.h:385
virtual bool getChannelContent(const RsGxsGroupId& channelId,
const std::set<RsGxsMessageId>& contentsIds, ...)
so the ids are now sent in batches of 200, sequentially per channel, with a
redraw between batches so posts appear as they arrive. A channel of 2000
items goes from 2000 requests to 10.
Splitting a batched answer needs all three lists walked rather than the
first non-empty one, so the storing moves into storePost / storeComment /
storeVote. They key off each item's own mMeta instead of the summary it was
requested from, which is the same value but no longer assumes a one to one
mapping between request and result.
The two responses are also checked before use: rsJsonApiRequest resolves to
undefined when the request fails, which the previous code dereferenced
straight away - the exact case that was happening here.
…t exist
A channel or board with no logo falls back to `data/streaming.png`:
src: cimage.mData.base64 === '' ? 'data/streaming.png' : ...
That file is in neither webui-src/assets nor the served tree, so the request
comes back 404 and the browser draws a broken image icon.
Both sites now render no <img> when there is nothing to show, and guard the
whole chain rather than comparing to '': mImage is absent rather than empty
on some groups, and `undefined.base64` throws during the view, which in
mithril takes the whole render down. The board side already had that guard,
it just kept aiming at the missing file.
These were the last two references to it - the post thumbnails on this
branch already go through channelThumbnailSrc / ChannelFallbackThumbnail.
Every own-identity lookup logs two errors in the console:
POST http://localhost:9092/rsIdentity/getOwnIds 404 (Not Found)
[RS] HTTP error: 404 Not Found
getOwnIds is the deprecated one and carries no @jsonapi annotation, so the
core does not expose it at all:
rsidentity.h:636
RS_DEPRECATED_FOR("getOwnSignedIds getOwnPseudonimousIds")
virtual bool getOwnIds(std::list<RsGxsId>& ownIds, bool only_signed_ids)
getOwnSignedIds and getOwnPseudonimousIds are the annotated ones
(rsidentity.h:458 and :466), so their union *is* the complete list rather
than a fallback for older cores.
people_util therefore drops the attempt and goes straight to the two calls
it was already making underneath. The catch that was meant to swallow the
failure never ran either: rsJsonApiRequest does not throw on a non-200, it
resolves with the status, so the code reached the union by way of
`Array.isArray(undefined)` being false. It worked by accident.
boards_util had the same call with no fallback at all, which made
voteForPost report "No identity found to vote." whenever no voterId was
passed. It now goes through people_util.ownIds(), so the endpoints and
their 30s cache live in one place.
Chat messages are carried as HTML. renderChatMessage() strips the tags but never decodes the entities left behind, and the result goes into a mithril text node, so they reach the screen verbatim. The visible case is a plain space: RetroShare sends the message as HTML as soon as the composer holds any formatting, and QTextDocument::toHtml() turns leading and repeated spaces into , which the web UI then displays as that literal string. &, < and > were affected the same way. The three call sites that turned HTML into text carried the same sequence of replacements, so they now share htmlToText(). Decoding goes through a textarea rather than a div on purpose: the content model of a textarea is plain text, so no part of a message can be parsed into an element. Nothing is trusted as markup at any point. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stop calling the deprecated /rsIdentity/getOwnIds (404 on every lookup)
Decode HTML entities in chat messages
Fix the request storm that makes channels unusable
…eir choosing renderChatMessage turns any <img src="..."> found in a received message into a real <img>, and the browser then goes and gets it. On a distant chat -- carried by a turtle tunnel precisely so that neither end knows where the other is -- that hands the reader's address to whatever host the sender picked, and tells them the moment the message was read. Pictures embedded by RetroShare travel as data: URIs; anything else is now shown as the text it is rather than fetched. The rest of this commit is the People tab paying for its lists. "All Users" is every identity the node has ever seen -- this profile's gxsid_db is 35 MB -- and the sidebar rendered all of them, firing one getIdDetails per row from inside its own view. The list is capped at 200 rows with a line saying how many are left, and the search narrows it. jdenticon redraws an avatar's SVG on every call, and that call sat in the view of every avatar on screen: once per avatar per redraw, and a redraw happens on every answer of every poll. Same id and same size, same drawing -- memoised. The history browser is mounted with the page and draws nothing until asked for, so its oninit was the moment a *conversation* opened, not the moment somebody wanted the history: opening a chat ran "give me every message ever stored" for a panel nobody had asked for. It loads when it opens now, which also fixes the chat page, where the panel was loaded once at mount and never again. Its overlay follows the three others onto dvh. Two small ones: adding or removing a contact from the context menu reloaded the identity summaries alone, while isContact is read from rs.userList.userMap, which only loadUsers() refreshes -- so the list kept showing the old state; and about 250 lines of unreachable code are gone, people_own_contacts.js entirely, the "Own Identities" widget of people_ownids.js, the second search box of people_util.js and the isSearched marking those two shared, which contactlist() and sortUsers() still wrote from inside a view. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
People tab: distant chat repairs and a full audit pass
The Refresh button was guarded by a `loading` flag set once at oninit and never raised again, so it only ever disabled itself before the first answer. After that every click fired two more requests, on top of the five second interval, with nothing preventing two runs from overlapping -- and on a phone they do overlap, since each answer closes its connection and the core serves them one at a time. `load()` now refuses to start while one is running and always clears the flag, including on the paths that throw. Friend names came from a second collection of the friend list -- getFriendList plus one getPeerDetails per friend, all at once -- run exactly once, at oninit: a friend added afterwards stayed `Peer 1a2b3c4d…` until the page was reloaded. network_data already does that work and keeps it in NetworkData.gpgDetails for the whole session, so the page reads that and asks for a refresh at most once a minute rather than never. `formatBytes` was a second implementation of `rs.formatBytes`, printing MiB where the statusbar, the chat and the file lists print MB for the same number. The pie segments are rotated back to twelve o'clock, where a pie chart is read from; the totals in the middle stay upright.
The address fallback deduces "local or external" from the RFC1918 ranges, but the core already says which is which: getPeerDetails appends its own marker to every ipAddressList entry, " 123 sec loc" or " 123 sec ext" (p3peers.cc). Guessing instead gets two cases wrong -- a peer behind CGNAT (100.64/10) or a double NAT has a private looking external address, and a loopback entry is not external at all -- where the marker is always right. The parser was also IPv4 only, which misses the very case the fallback exists for: GetRetroshareInvite() clears extAddr and moves the address into ipAddressList when it is IPv6, because the certificate format only carries IPv4 numbers, so a peer added by short invite over IPv6 has an empty external address and an entry the regex could not read. RsUrl wraps IPv6 hosts in brackets and escapes the % of a link-local scope id as %25; both are handled, and the entry with no marker at all -- the one that invite path produces -- is external by construction.
Statistics page: overlapping polls, a second friend collection, a second formatBytes
…r-121 Peer addresses: read the core's loc/ext marker, and handle IPv6
…like one The overlay carries role=dialog and aria-modal=true, but its Escape handler sits on the overlay itself and therefore only sees what bubbles through it. The close button is focused on open, so Escape works -- until the first tap on the picture, which moves the focus to <body>: from then on the only way out is the close button or the Back button. The handler moves to the document, in the capture phase, and is removed when the preview closes. Tab is caught there too: aria-modal promises the rest of the page is inert, and without it the focus walks straight out of the preview into the message list behind it. Closing also puts the focus back on whatever opened the preview rather than dropping it on <body>.
markReadLocally() clears the whole 0xf0 nibble, but RS_MSG_TRASH is 0x20 and lives inside it (rsmail.h). Opening a message from the Trash therefore cleared its trash flag as well, and the row showed as an ordinary read mail until the next load. Only the two unread bits are cleared now. The badge itself is read from the navigation view, so unreadCount() ran a full pass over the inbox on every redraw -- once for the rail and twice more for the bottom bar, which asks for the value twice per item. The count is kept instead, recomputed when the inbox changes: after a load, and after a message is marked read.
- Fixed to get more space for the text field
The message pane scrolled to the bottom from its onupdate, unconditionally. That hook runs on every redraw of the chat hub -- an incoming message, a poll answer, a keystroke in the composer -- so scrolling up to read anything older was undone a few tens of milliseconds later, every time. It now follows the reader: the pane stays pinned while it is already at the bottom, stops as soon as it is scrolled away from it, and pins again when it comes back. Sending a message still jumps down whatever the state, and switching room opens on its last message, since the pane is reused rather than recreated and its oncreate does not run again. The scroll handler sets event.redraw = false: mithril redraws after every handler by default, and this one fires continuously while the pane is dragged.
A room opens on its last 20 messages and that was all there ever was: reaching the top of the pane loaded nothing, so anything older was only reachable through the separate history browser. Scrolling near the top now asks for a larger slice. p3HistoryMgr::getMessages takes a count and nothing else -- no cursor, no "before this message" -- and always answers with the newest ones, so the only way to reach older text is to ask for more and let addMessages() drop what is already held. It re-sends what we have, which is the price of that API; the slice is small, and the core keeps ten days at most anyway. An answer shorter than the count asked for means there is nothing older left, and the pane stops asking. The opening slice stays at 20: every room opening pays for it, and on a phone each request is a fresh connection on a core that answers one at a time. Older messages are inserted above what is on screen, which would push the conversation down by their height; that height goes back into scrollTop, so the reader does not move while a slice lands.
…for-121 Image preview: Escape, Tab and focus restore
Mail badge: reading a message must not untrash it, and stop recounting per redraw
…for-121 Chat rooms: read further back by scrolling up
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
5c6e2f7

