Improved the webui pages for phone - #121
Conversation
Added Board posts display Added for Network to list last Chats
Fixed css issues
|
@defnax I feel there are some other |
|
I hate this scss, i get very often destroyed corrupted this |
Sure take a look when you get chance. So, we need to be careful with the |
|
But i commited all changes what i see in my git app. |
* 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
|
@zelfroster new review? |
|
@defnax I was working on streamlining some things and adding instructions, so the styles don't get messed up each time. Let me push the changes in a bit, we can merge that, and then after you update this PR accordingly, will review it. |
|
yes this issue with styles i get headdache, then i lose motivation when its destroyed and losing time |
|
Defnax please rebase this pr/121 on master so that it gets a chance to be reviewed and merged, and I can work on it. |
@jolavillette @defnax What's the plan for webui rn btw? I mean broadly do we already have planned features to implement or things to improve? Since, we are going to use AI most probably to improve things here, it would be better to add an AGENTS.md with some specific instructions here too, else the webui product as a whole would have inconsistent looking UI/UX. |
|
As far as I am concerned:
May I respecfully suggest that you also use AI to make a first pass on PRs, so that you can focus on what is worth your human time? IMO that's the way everything should go now in the whole RetroShare project. |
Makes sense 👍🏼
Yeah, that's a great suggestion, I was thinking of doing that too. @csoler I was wondering if we can get an AI tool reviewer to automatically review PRs so the author's can fix the obvious issues etc, and then the actual reviewer can take a better look to save everyone's time and accelerate the work on webui maybe. |
|
This one is a good AI reviewer to use if we can get it for our project - https://www.greptile.com/open-source |
|
of course if that can be helpful. What we should avoid absolutely is the AI merging PRs by itself or even being able to modify the code in the repository. |
I had no time now my code broken i has some conflicts on my code with master |
|
I think after this merged i will make a break with webui |
OK I will rebase it on master |
Cause: the thread view created a second full-height .widget inside the page’s existing .widget. The nested height and overflow rules caused long posts to extend into a clipped area. Changes: - Removed the nested full-height widget The main Forums widget is now the single vertical scroll area - Added safe wrapping for long text - Added horizontal scrolling for wide code blocks and tables - Constrained images, videos, and embeds to the available width
Improved create channel post
|
@jolavillette there is a issue with forums i solved scroll issue cant yet fix when opening first time a forum then it fails but second time it works maybe claude can find and a fix?
|
- Added persistent avatar preview - Shows a deterministic jdenticon by default - Jdenticon updates based on the entered identity name - Added custom avatar selection - Shows selected avatar immediately - Added “Use default” to remove the custom avatar - Custom avatar is sent for linked and pseudonymous identities - Updated responsive desktop/mobile layout
Opening a forum threw, and every redraw after it threw again:
TypeError: Cannot read properties of undefined (reading 'view')
at initComponent
NotFoundError: Failed to execute 'removeChild' on 'Node'
userList.userMap holds {name, isContact} objects (rswebui.js:300 and :317),
not strings. userList.username() is the accessor that unwraps them:
const name = typeof entry === 'object' ? entry.name : entry;
but several views read the map directly and handed the raw object to mithril:
fauthor = rs.userList.userMap[forumDetails.author]; // forum_view.js
...
m('p', m('b', 'Admin: '), fauthor)
Vnode.normalize returns anything that is `typeof === 'object'` untouched, and
createNode sends every non-string tag to createComponent, so the object was
treated as a component and initComponent dereferenced `vnode.tag.view` on
undefined. Once a redraw throws inside createNodes the vdom no longer matches
the DOM, which is where the removeChild storm comes from: the statusbar, the
identity bulk fetch and the forum load all redraw, and all of them fail.
It only bites when the author is already in the identity cache, which is why it
looks intermittent: the render that breaks is the one triggered by fetchBulk's
redraw, when the entry flips from missing to object.
Same direct read, same crash, in the channel view (header author, and the
comment author cell) and in the board view. The identity selectors called
.toLocaleString() on the entry, which does not throw but prints
"[object Object]" as the option label.
All of them now go through username(), which returns the name when it is known
and the raw id otherwise, and which queues the missing ids for the next bulk
fetch on the way.
…r-121 Fix the forum crash: never put a userMap entry straight into a view
- Added a default Forum thumbnail to the forum details card. - Added a default Board thumbnail for boards without an uploaded image. - Added a default Channel thumbnail when no image is set.
A vnode carries the DOM node it owns, so the same one must not be rendered
twice. popupMessage is handed a ready made vnode and now mounts it, which
re-renders it on every global redraw, so freshVnode() rebuilds it on each pass.
It only rebuilt the root though: `m(vnode.tag, vnode.attrs, vnode.children)`
passes the children array by reference, and updateNodes() starts with
if (old === vnodes ...) return
so mithril skips the entire subtree. Everything below the first level of a modal
is therefore frozen at its first render — which goes unnoticed today because the
popups built as plain vnode trees are all static messages, and everything that
has to update is passed as a component. It is a trap for the next one.
The clone is now recursive, with the three tags that are not selectors handled
through their own factory: '<' is m.trust, and rebuilding it with m() would
silently turn trusted html into an empty div, since '<' matches nothing in the
selector parser; '[' is m.fragment; '#' is a text vnode whose children is the
string itself.
Checked that no popup built as a plain tree contains an input, textarea or
select, so nothing starts having its value re-applied under the user's fingers:
every form modal goes through a component, which re-renders on its own.
The version label assumes the loader robustness PR lands first.
Clone modal content all the way down
Three defects of the same family, in the loading path. 1. rsJsonApiRequest sets connectionState.status = false on *any* status other than 200. But an answer, whatever its code, proves the core is there. A 404 on an endpoint this build does not expose, a 401 on a stale password: none of them is a lost connection. Only status 0, no HTTP response at all, is. This is visible today: getBoardPostSummaries only exists in an unmerged libretroshare branch, so every board load 404s on a stock core and the status LED blinks red before the fallback runs. Any optional endpoint we probe from now on has the same effect. The flag is now false only on status 0, and the last HTTP status is recorded in extract() so it survives a body that fails to parse. 2. Reaching .catch() after a valid 200 means the response was cut short, not that the core went away. connectionState now stays true there. That matters because a truncated response is exactly what the JSON API produces when it cannot flush a large answer in time, and the loaders react to that flag. 3. The channel loader splits a failed batch in two until the offending post is isolated, which is right when the response was too large, and catastrophic when the core is unreachable: every half fails too, so one batch of 25 turns into 2N-1 = 49 doomed requests, ~4000 for a 2000 item channel. It now stops splitting when connectionState is false, which points 1 and 2 made accurate. The board loader had no splitting at all: it ignored the boolean updateContent already returned, so a truncated batch of 25 posts vanished on a single console.warn. It now uses the same helper as the channels.
An HTTP error is not a lost connection
Mobile browsers count the collapsible URL bar in 100vh, so an overlay sized that way is taller than the visible area: its bottom, where the buttons usually are, sits under the fold and cannot be reached. dvh follows the bar as it retracts. Three sites, all fixed overlays: the global modal backdrop (#popupmessage) and the two chat dialog overlays. The vh line is kept above the dvh one as a fallback for engines that do not know the unit and would otherwise drop the declaration entirely. styles.css regenerated with the pinned sass 1.97.3.
dvh for the full screen overlays
Three defects in the new thread composer. The 199 000 limit of a GXS message is a *byte* count, but the composer compares it against String.length, which counts UTF-16 units: an accented letter is two bytes for one unit, an emoji four bytes for two. With an emoji picker one click away in that very toolbar, the counter can report room left on a message the core will refuse. It now measures UTF-8 through TextEncoder, and says bytes rather than characters. postBody() was called five times per render -- twice for the class, twice for the counter text, once for the disabled state -- and it re-escapes the message and re-joins every inline image, each of which is up to 175 KB of base64. That ran on every global redraw, which the statusbar triggers continuously, while the user is typing. It is now built once per pass. pollFileHash() re-arms itself every 500 ms for up to a minute. Closing the composer left it running: it kept polling and redrawing a component that is no longer mounted. onremove now stops it.
Since the list was reduced to metadata, ChannelView.oninit fetches the content of the channel being opened. oninit runs again on every visit though, with no guard, so stepping in and out of a 2000 item channel redownloaded all of it, images included, each time. The content is now pulled only when it is missing from memory or older than a minute, so posts published meanwhile still appear without forcing the user to reload the page. The call sites that publish or delete call updatedisplaychannels directly and keep refreshing unconditionally. The timestamps live at module level: the component is rebuilt at every visit, a field of it would forget immediately.
eslint reported them and nothing references them: they are the previous generation of UI, superseded in place. channels/channel_view.js displaycomment() and the AddComment form it opened, replaced by renderComment() and ChannelComments. displaycomment only referenced itself, recursively, which is why it looked used. chat/chat.js LayoutSingle, the single chat room layout that predates the hub, and with it LobbyList, Lobby, SubscribedLobbies and PublicLobbies, which nothing else called. Six names imported from chat_state were unused as well. Kept as its own commit so it can be reverted alone if any of it turns out to be wanted again.
The last three no-useless-assignment errors. Each declares a value that every path reassigns before reading it: the exhaustive if/else of customState, the network/chats branch of displayFriends, and the quality loop of result, which runs at least once and throws below it. The web UI now lints clean: 0 errors, 0 warnings, where the branch point had 17.
… never fires The version and the short invite of a node do not change while the web UI is open, but the dialog asked the core again on every open, showing "Loading..." each time. They are now cached by node id. The two .catch() handlers could not do what they were written for: rsJsonApiRequest never rejects, it resolves undefined when a request fails. They only ever ran by accident, when reading .body of that undefined threw a TypeError inside the .then. The failure is now read off the resolved value, where it actually is.
rsJsonApiRequest resolves undefined when a request fails. Around forty call sites go straight for res.body.retval, so every failure threw a TypeError -- inside an onclick most of the time, where nothing catches it: the button does nothing at all, and the console shows a stack about `body` rather than a failed request. It now resolves the same shape as a real answer, with an empty body. Every defensive check in the code base tests res.body or res.body.retval, so an empty body still reads as a failure to all of them, including the batch loaders that key their split-retry off it.
urlParams.get('Url') || window.location.protocol === 'file:'
? 'http://127.0.0.1:9092'
: <origin>
=== binds tighter than ||, so the test reads
(Url || protocol === 'file:') ? default : origin
and passing ?Url= made the condition true, which selected the hardcoded default
and discarded the value given -- the one case the parameter exists for. Username
and Password, read the same way just above, worked; only Url did not.
Parenthesised so the parameter wins when present, and the file:// and served
cases keep the behaviour they had.
Audit pass: composer limit, channel reloads, dead code, failed requests
Clicking the emoji button opened the panel, and clicking inside it did insert an emoji, but the panel looked empty: the emojis were being drawn, in white, over white. The global `button` rule applies the button() mixin, which sets `color: white` to sit on a coloured background. The picker resets the parts of it that clash -- border, background, box-shadow -- but not the colour, and its own background is #fff. The category row above the grid was invisible for the same reason. Found by testing in a browser: nothing in the sources says a button is white until you follow the mixin.
…or-121 The forum emoji picker was drawing white emojis on a white panel
Uh oh!
There was an error while loading. Please reload this page.