Skip to content

Improved the webui pages for phone - #121

Open
defnax wants to merge 66 commits into
RetroShare:masterfrom
defnax:improvements_v2
Open

Improved the webui pages for phone#121
defnax wants to merge 66 commits into
RetroShare:masterfrom
defnax:improvements_v2

Conversation

@defnax

@defnax defnax commented Jul 26, 2026

Copy link
Copy Markdown
Contributor
  • Added Board posts display
  • Added comments viewer & composer for boards
  • Added for Network to list last Chats
  • Improved Channels for Phone & Desktop
  • Fixed channels thumbnail layout issue
  • Added same comments design from boards to channels
  • Added Navbar for webui on phones
  • Fixed config pages get compatible for phone
  • Fixed layout issues for myfiles, friendfiles & search

defnax added 3 commits July 26, 2026 20:50
Added Board posts display
Added for Network to list last Chats
@zelfroster

Copy link
Copy Markdown
Contributor

@defnax I feel there are some other .scss files you have not pushed yet. Can you check if there are .scss files in your local and push them?

@defnax

defnax commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

I hate this scss, i get very often destroyed corrupted this
I need look tonight

@zelfroster

Copy link
Copy Markdown
Contributor

I hate this scss, i get very often destroyed corrupted this I need look tonight

Sure take a look when you get chance.
Also, think of .scss as the source files, and the styles.css as the output.

So, we need to be careful with the .scss always and commit them whenever pushing changes.

@defnax

defnax commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

But i commited all changes what i see in my git app.
need look tonight if something is missed

defnax added 8 commits July 27, 2026 19:23
* 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
@defnax

defnax commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

@zelfroster new review?

@zelfroster

Copy link
Copy Markdown
Contributor

@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.

@defnax

defnax commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

yes this issue with styles i get headdache, then i lose motivation when its destroyed and losing time

@defnax defnax changed the title Improved for phone the webui pages Improved the webui pages for phone Aug 4, 2026
@jolavillette

jolavillette commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

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.
If you are too busy for that tell me, and I will make a pr to your branch to rebase it.

@zelfroster

Copy link
Copy Markdown
Contributor

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. If you are too busy for that tell me, and I will make a pr to your branch to rebase 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.

@jolavillette

Copy link
Copy Markdown
Contributor

As far as I am concerned:

  • bug fixing, there are many, see for example pr/123, they are easy to review and merge
  • usability on phone/tablet, because until an mobile version of RS is released the webui all we have

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.

@zelfroster

Copy link
Copy Markdown
Contributor

As far as I am concerned:

  • bug fixing, there are many, see for example pr/123, they are easy to review and merge
  • usability on phone/tablet, because until an mobile version of RS is released the webui all we have

Makes sense 👍🏼

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.

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.

@zelfroster

Copy link
Copy Markdown
Contributor

This one is a good AI reviewer to use if we can get it for our project - https://www.greptile.com/open-source

@csoler

csoler commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

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.

@defnax

defnax commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

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. If you are too busy for that tell me, and I will make a pr to your branch to rebase it.

I had no time now my code broken i has some conflicts on my code with master
But now my pr is more useable on phone too
I think now more useable webui then before.

@defnax

defnax commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

I think after this merged i will make a break with webui
I don’t know if get solved with rebase i tried on thursday no success yet i will try tonight

@jolavillette

Copy link
Copy Markdown
Contributor

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. If you are too busy for that tell me, and I will make a pr to your branch to rebase it.

I had no time now my code broken i has some conflicts on my code with master

OK I will rebase it on master

defnax added 6 commits August 12, 2026 18:39
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
@defnax

defnax commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@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?

app.js:2131 TypeError: Cannot read properties of undefined (reading 'view')
at initComponent (app.js:1283:29)
at createComponent (app.js:1306:7)
at createNode (app.js:1206:14)
at createNodes (app.js:1184:11)
at createElement (app.js:1276:11)
at createNode (app.js:1204:13)
at createNodes (app.js:1184:11)
at createElement (app.js:1276:11)
at createNode (app.js:1204:13)
at createNodes (app.js:1184:11)
sync @ app.js:2131
(anonymous) @ app.js:2141
requestAnimationFrame
redraw @ app.js:2139
lastUpdate @ app.js:2653
loop @ app.js:2668
resolveRoute @ app.js:2637
setTimeout
fireAsync @ app.js:2684
route.set @ app.js:2727
onclick @ app.js:14354
EventDict.handleEvent @ app.js:2012
app.js:2131 NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

defnax and others added 23 commits August 13, 2026 11:01
- 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.
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.
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.
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants