Commit Graph

9353 Commits

Author SHA1 Message Date
jolavillette
52cefc693e gui/main: fix Linux/macOS build, applicationFilePath was declared under WINDOWS_SYS only
Commit e63584843 declared applicationFilePath inside the WINDOWS_SYS
block but uses it unconditionally to fill conf.main_executable_path,
so every non-Windows GUI build fails with 'applicationFilePath was
not declared in this scope'. Declare it at function scope, seeded
from argv[0] (the previous non-Windows behaviour); Windows still
overrides it with QCoreApplication::applicationFilePath().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 07:53:07 +02:00
csoler
799d4c9197
Merge pull request #3281 from thunder2/fix-tor
Fix tor
2026-09-03 22:32:07 +02:00
csoler
03daec3e2a
Merge pull request #3297 from jolavillette/fix/distant-chat-locally-closed-event
Distant chat window: close it when the conversation is closed from another client
2026-09-03 18:48:09 +02:00
jolavillette
040d19c946 ShareManager: stop discarding pending edits on every friend-list event
The "Configure shared directories" dialog edits a local copy of the
shared directories and only commits it when Apply is pressed. Since
0ce2dd848 it reloads that copy from the core on *any* FRIEND_LIST event,
while the intent was only to follow friend-group changes. NODE_CONNECTED,
NODE_STATUS_CHANGED, NODE_STATE_STRING_CHANGED... fire every few seconds
as soon as a friend is online, so a directory added or removed in the
dialog reverts to the saved state before the user can press Apply.

Only react to GROUP_ADDED/REMOVED/CHANGED, and on those refresh the group
names column (dropping groups that no longer exist) instead of reloading
everything. Also do not reload when showYourself() is called while the
dialog is already open, for the same reason.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 05:47:37 +02:00
jolavillette
ea97a7437d Distant chat window: close it when the conversation is closed from another client
A distant chat started from the web UI pops the desktop chat window for
its tunnel (the core sends itself a "[Starting distant chat" message).
Leaving that conversation from the web UI then left the window as it was:
green LED, "You can talk", and on close the offer to end a conversation
the core no longer had -- closeEvent() ignored the false answer of
getDistantChatStatus() and read a default-initialised status.

On the new RsDistantChatEventCode::TUNNEL_STATUS_LOCALLY_CLOSED
(libretroshare fix/distant-chat-locally-closed-event) the window closes,
as it does when the conversation is ended from the window itself. And
closeEvent() asks no confirmation and calls no close for a tunnel the
core does not know any more.
2026-08-30 09:20:37 +02:00
csoler
f8553d2fd9
Merge pull request #3276 from csoler/v0.6-IdentityDialog3
fixed warning message about identities with insecure fingerprint
2026-08-28 12:21:25 +02:00
csoler
5bbe63e4d4
Merge pull request #3292 from jolavillette/fix/eliditemdelegate-font-inherit
gui: let RSElidedItemDelegate inherit the view font from the model font
2026-08-28 11:14:46 +02:00
jolavillette
f0a5a4687f Fix "Accept friend request" button persisting after SSL-only friend added
In the Activity feed, accepting an unknown peer (no PGP key in keyring)
adds it as an SSL-only friend via addSslOnlyFriend(), which sets
skip_pgp_signature_validation instead of the PGP-keyring accept_connection
flag. SecurityItem::updateItem() only tested details.accept_connection to
decide button visibility, so for an accepted SSL-only friend the condition
stayed false and the "Accept friend request" button kept being shown (and
"Remove friend" stayed hidden), even though the peer was already a friend.

Treat skip_pgp_signature_validation == true as accepted too, since that
flag is only set for a peer already present in the friend list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-16 18:24:35 +02:00
jolavillette
61e647081c gui: let RSElidedItemDelegate inherit the view font from the model font
The delegate took the Qt::FontRole of the model and assigned it to the style option as
is, replacing the font of the view instead of merging into it. A font stored in a model
usually only carries the one attribute the caller wanted to change — most of the time
bold — and expects family and size to keep coming from the view. Assigning it whole
dropped those items back to the application font, ignoring the font size configured in
the settings, which FontSizeHandler puts on the view.

That is easy to trigger: QTreeWidgetItem::font() returns a default constructed font when
the item has no Qt::FontRole yet, so the familiar "read the font, set it bold, write it
back" turned the whole list into the application font. On a remote X display where the
desktop font is 9pt and the RetroShare font size is 11pt, the list visibly shrank.

Merge with QFont::resolve() instead, which keeps only the attributes the model really
set and takes the rest from the view — the same merge QStyledItemDelegate::initStyleOption()
performs. Same change in the tooltip path of editorEvent(), which recomputes the elision
with that font. sizeHint() was already using the merged font from initStyleOption(), so
rows were being measured with one font and painted with another.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 18:23:58 +02:00
csoler
6160b39dab
Merge pull request #3271 from defnax/distantchat-popup-fix
💬 Fixed to not popup distant chat on incoming chat
2026-08-16 14:16:35 +02:00
jolavillette
9119058c48 GUI: persist search results column widths across restarts 2026-08-07 01:12:19 +02:00
jolavillette
1b99beebc5 Address review: pointer hand-off to the workers, event-driven board update
- GxsForumModel / PostedPostsModel: hand the id list to the background
  worker through a pointer deleted after the call, so the lambda capture
  does not deep copy it.
- PostedPostsModel: setAllMsgReadStatus() no longer updates the model
  directly. The batched READ_STATUS_CHANGED event now carries the
  affected ids and their new state (libretroshare side), and the event
  handler applies them locally — so a batch initiated by another
  frontend (e.g. webUI) updates the board view exactly the same way,
  and no post is re-read from the database.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
ebac1481f0 Revert "gui(forums): opt-in latency probes on the read/unread and loading path"
Profiling code removed from the PR, as requested in review of the sibling
PRs. This reverts commit 8845aa4636e630977f4a6817e8a267735e128e28.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
dea2f28dd9 gui(forums): opt-in latency probes on the read/unread and loading path
Set RS_GUI_PROFILE to a threshold in milliseconds (0 reports
everything) to get one line per measured operation on stderr, plus a
50 ms watchdog on the GUI thread that reports every stall of the event
loop wherever the blocking code lives, naming the last probed
operation. This is what located the actual freeze of the read/unread
path (a whole-model dataChanged() measured at 1072 ms) after several
plausible-from-code-reading fixes had failed to: the numbers say where
the time goes instead of a story explaining where it might go.

All probes are inert unless the environment variable is set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
5cbcd962dd gui(forums): re-read the group on SUBSCRIBE_STATUS_CHANGED instead of flipping the flag
The event means the subscription status changed, not that it was
toggled: guessing the new value by inverting the current flag goes
wrong as soon as one event is duplicated, lost, or refers to a state
the widget already holds. When the guess lands on 'unsubscribed', the
widget silently refuses to mark posts read or unread (the very first
check of markMsgAsReadUnread) until the next group reload puts the flag
right again. updateGroupData() was already called anyway; let it be the
only writer of the flag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
b0ca91c825 gui(forums): drop the expanded-items save/restore around read-status changes
setMsgReadStatus() only emits dataChanged(); it never resets the model
nor changes its layout, so the expanded items and the current index
survive it untouched. Saving and restoring them on every toggle was
pure overhead, and an expensive one: the restore walks every expanded
item and pays, for each, a linear scan of the post array plus a linear
scan of the view items -- quadratic work on the GUI thread for a single
post marked read or unread on a large forum.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
243c417bfc GxsForumModel: size the read-status view refresh on rows, not message ids
280e51f53 restricted the dataChanged() refresh to the affected post
unless more than 5 messages changed, in which case it refreshes the
whole view. But changed_msgs counts message ids, and a post carries one
id per stored version of itself, all sharing one read status: marking a
single post read or unread on a post edited 46 times queued 47 ids and
triggered the whole-model refresh. Measured on a 8259 post forum: one
click, one row repainted, 1072 ms of frozen GUI in the dataChanged()
handling; with this fix the same click costs about 1 ms.

Count the changed rows separately and size the refresh on that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
3860f76dc1 gui(forums): create reload timer before setGroupId (fix ctor SIGSEGV)
The coalescing timer was allocated near the end of the constructor, but
setGroupId(forumId) is called earlier and synchronously reaches
groupIdChanged() -> updateDisplay(true) -> mDeferredReloadTimer->stop(),
dereferencing the still-uninitialised pointer and crashing the moment a forum
was opened. Allocate the timer right after setupUi(), before setGroupId().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
839293f84d gui(forums): coalesce full-forum reloads to stop sync-burst UI freezes
handleEvent_main_thread() ran a full updateDisplay(true) (updateForum ->
getForumPostsHierarchy -> setPosts full reset -> proxy re-sort/re-filter ->
tree rebuild) for every incoming GXS event. A sync batch delivers many
NEW_MESSAGE events back to back, so the forum was reloaded once per post,
freezing the UI for seconds and resetting the model out from under the user's
current selection (and racing the async read-status persistence, making a
just-read post pop back to bold).

Route those events through a single-shot QTimer (300 ms) that is restarted on
each event, so a whole burst collapses into one reload once the events settle.
An explicit reload (forum switch) cancels any pending deferred one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
78ff5d66e6 GxsForumModel: fix malformed model reset dropping freshly-arrived posts
setPosts() called beginResetModel()/endResetModel() *before* swapping mPosts,
so endResetModel() fired while the model still exposed the previous hierarchy,
then emitted an extra beginInsertRows()/endInsertRows() on top of the reset.
That double-signalling left the view/proxy row->source mapping stale, so a
post that had just arrived could map to an invalid source index: selecting it
neither displayed its content nor cleared its unread (bold) state.

Bracket the actual data swap with beginResetModel()/endResetModel() and drop
the bogus row-insertion signal; a full reset already tells the views to
re-query everything. Same anti-pattern as the channels grid-empty-on-new-post
fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
8959b45087 gui(forums): keep single-post read latency low via unitary markRead
Routing every read through the batched markRead(grpId, vector, read) also
routed single interactive reads (selecting/opening one post) through its
waitToken(): the READ_STATUS_CHANGED event, and thus the forum-list unread
counter refresh, was only emitted once the DB write completed, adding
~100ms plus one GXS tick of latency versus the previous behaviour.

Send a single changed message through the per-message markRead(pair),
which emits the event immediately, and keep the batched call only for
larger sets (mark-all, with-children, versioned posts) where deferring the
single event until completion is what avoids the freeze.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
2816dfd9f6 gui(forums): fix slow post navigation by restricting dataChanged refresh to affected post and parents 2026-08-01 23:16:08 +02:00
jolavillette
e5ed16c805 Channels/Posted models: never freeze the UI on "mark all as read"
GxsChannelPostsModel and PostedPostsModel spawned one detached std::thread (and
got one event) per post on "mark all as read/unread", freezing the UI on large
channels/boards - the same problem just fixed for forums, and worse here given
the much larger channel/board databases.

Both now collect the affected message ids and issue a single background batch
call (setMessageReadStatus / setPostReadStatus with a vector) plus a single view
refresh. PostedPostsModel additionally updates its local model up front (it
previously relied entirely on the per-message events).

Requires the matching libretroshare bulk overloads.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
f4463dd17f GxsForumModel: never freeze the UI on "mark all as read"
recursSetMsgReadStatus() used to spawn one detached std::thread per changed
post (each blocking up to 5s in markRead) and emit one dataChanged() per post.
On a forum with thousands of unread posts this created thousands of threads
and signals driven from the GUI thread, freezing (and sometimes crashing) the
UI - the reported symptom being an hour-long hourglass on an 8000-post forum.

Now the recursion only collects the affected message ids and updates the
in-memory model; setMsgReadStatus() then issues a single background batch call
(rsGxsForums->markRead(group, ids, read)) and a single view refresh. The GUI
thread does O(n) in-memory work plus one thread and one signal, so it stays
responsive no matter how large the forum is.

Requires the matching libretroshare bulk markRead() overload.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 23:16:08 +02:00
jolavillette
0e31d5fed0 GUI: remove the profiling probes from the channel post model
Requested in review: the RS_GXS_PROFILE instrumentation of update_posts()
and setPosts() served to measure the freeze and verify the fix, but it
should not stay in the optimised code. The libretroshare profiling header
is no longer included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 14:31:38 +02:00
jolavillette
28e7f791ff GUI: stop copying and sorting the channel post array in the Qt thread
setPosts() runs in the Qt thread, through postToObject(). It copied the whole
post array a fourth time and then sorted it there, so for a channel with a few
thousand posts the interface was frozen for the duration of both.

Move the sort into the loader thread in update_posts(), where the array is
already sitting after the service call, and have setPosts() take its argument
by rvalue reference so the array is moved in rather than copied. The sort is
kept in setPosts() as a safety net: on an already ordered array it only runs
comparisons and moves nothing.

Combined with the libretroshare side making posts movable, the array is now
handed from the data store to the model without a single deep copy of a
thumbnail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:34:12 +02:00
jolavillette
54d4c791aa GUI: profile the channel post model loading
Complements the GXS side instrumentation (libretroshare, gxs/rsgxsprofiler.h)
with the two phases that happen in the GUI: the service calls made from the
loader thread in update_posts(), and the model update in setPosts(), which
runs in the Qt thread and is what actually freezes the interface.

setPosts() reports its three phases separately (array copy, sort, view update)
since they have very different causes.

Enabled by the same RS_GXS_PROFILE environment variable, off by default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:34:12 +02:00
thunder2
2c9fcd0c57 Moved tor data directory for gui to account directory so that we can run multiple tor instances simultaneously 2026-07-27 19:07:57 +02:00
thunder2
e635848438 Use “QCoreApplication::applicationFilePath” instead of argv[0] for the name of the executable file, as argv[0] does not always contain the full path on Windows 2026-07-27 11:15:16 +02:00
csoler
d57ecdeea7 fixed warning message about identities with insecure fingerprint 2026-07-18 20:42:03 +02:00
defnax
7f15e396ac Fixed to not popup distant chat on incoming chat 2026-07-09 09:30:53 +02:00
jolavillette
923fe3338a fix(cmake): reliably prefer Qt6 over Qt5 in GUI detection
find_package(QT NAMES Qt6 Qt5 ...) does not honour the NAMES order on
distros that ship Qt5 and Qt6 side by side in the same prefix (Debian/
Ubuntu multiarch /usr/lib/<arch>/cmake): CMake globs the sibling
Qt5*/Qt6* config dirs and stops at whichever <name>Config.cmake it
reaches first, which is filesystem-order dependent and routinely lands
on Qt5 even though Qt6 is listed first.

Probe Qt6 explicitly (find_package(Qt6 QUIET COMPONENTS Core)) and fall
back to Qt5. This makes the Qt6 preference deterministic and also makes
-DCMAKE_DISABLE_FIND_PACKAGE_Qt6=ON work as a reliable Qt5-force switch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 22:12:14 +02:00
csoler
3081f530ca
Merge pull request #3258 from csoler/v0.6-IdentityDialog3
added warning for identities using deprecated signature format
2026-07-08 21:33:51 +02:00
jolavillette
711589283a filter joined/left system messages in chat rooms 2026-07-06 22:07:44 +02:00
Gioacchino Mazzurco
bb47e094b3
Merge pull request #3265 from jolavillette/fix/gui-own-version-macros
fix(gui): give RetroShare GUI its own version, independent from libretroshare
2026-07-05 18:20:38 +02:00
jolavillette
dd5add516e fix(gui): give RetroShare GUI its own version, independent from libretroshare
The GUI reported RS_HUMAN_READABLE_VERSION (a libretroshare macro) as "the
RetroShare version". In the CMake build libretroshare exports RS_MAJOR_VERSION
as a PUBLIC define, so the GUI inherited the engine's version and displayed it
as its own: About showed the same number for both.

Give the GUI its own version, as discussed:

- Add retroshare-gui/src/rsguiversion.h defining RS_GUI_VERSION with a built-in
  default, so every build path (including qmake/Android) compiles even without
  version injection.
- Inject RS_GUI_VERSION from 'git describe' of the super-project in both CMake
  (retroshare-gui/CMakeLists.txt) and qmake (retroshare.pri).
- RsApplication::retroshareVersion() now returns the GUI's own version instead
  of libretroshare's.
- About (HelpDialog) shows the GUI version at the top; libretroshare's version
  is listed among the other libraries, like any dependency. AboutWidget already
  displayed both lines and is now correct automatically.

libretroshare needs no change: it already exposes its own version through
RsInit::libRetroShareVersion() in both build systems.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 11:40:36 +02:00
jolavillette
2b1fb920af gui: replace GxsTransportStatistics 4s auto-refresh with manual Refresh
The RsAutoUpdatePage(4000) base class polled getGroupStatistics() every
4 seconds. That call issues blocking GXS token requests (up to 5s per
group) and had no anti-reentrancy guard, so background refreshes piled
up, saturated the GXS queue and froze the whole UI.

Revert to MainPage and drive updates on demand instead:
 - a Refresh button
 - showEvent(), so the page refreshes when navigated to
 - settingsChanged(), kept so a date-format change re-renders

Date normalization (formatDateTime) is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 11:14:17 +02:00
jolavillette
6e6e1403be gui: format dates in GxsTransportStatistics using DateTime and enable auto-update 2026-07-04 09:47:06 +02:00
csoler
6ec6de5d0f
Revert "Added colored links feature" 2026-07-03 23:13:00 +02:00
csoler
6a9c354a85
Merge pull request #3254 from defnax/colored-links-v5
Added colored links feature
2026-07-03 22:51:29 +02:00
csoler
874b320ae3 added a warning for channels to be re-edited due to admin signature checking problem 2026-07-03 16:36:03 +02:00
csoler
a17464c316 added warning for identities using deprecated signature format 2026-07-02 22:17:43 +02:00
jolavillette
874d204b85 fix(SharedFiles): stop SIGSEGV when toggling "Popular files" after a search
Searching a keyword and then clicking "Popular files" crashed in
QSortFilterProxyModel::parent() (via rowsAboutToBeRemoved).

SFDSortFilterProxyModel::setUploadedOnly() called invalidateFilter(),
which runs Qt's incremental filter_changed row-removal path over the
currently-mapped tree. After a search the tree is fully expanded and
carries live persistent proxy indexes; removing an interior directory
node frees the child mapping structs those indexes still point at, and
the removal bookkeeping then dereferences freed memory -> SIGSEGV. The
plain-search path never crashed because it is always preceded by a full
model reset; the "Popular files" toggle was the only path that
re-filtered without one (deliberately, to avoid a slow re-crawl).

Use invalidate() instead: it rebuilds the whole proxy mapping via the
layoutAboutToBeChanged/layoutChanged protocol, which remaps persistent
indexes safely and never emits rowsAboutToBeRemoved. It rebuilds only
the proxy mapping (lazy, near-instant) and does not re-crawl the source
model, so the performance the toggle path was protecting is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 20:08:38 +02:00
defnax
460afb7824
fix qt6 compile 2026-06-27 15:33:28 +02:00
defnax
177141a042 Fix qt6 compile 2026-06-27 15:07:34 +02:00
defnax
74b8ebd31a Added colored links feature
Fixed to not send the color, only locally
2026-06-27 14:42:55 +02:00
csoler
d20c783769
Merge pull request #3245 from jolavillette/fix/qt6-qdatetime-settime_t
Qt6: replace removed QDateTime::setTime_t() with setSecsSinceEpoch()
2026-06-25 13:40:22 +02:00
csoler
78aa3f4d14
Merge pull request #3241 from jolavillette/fix/rsgraphwidget-area-fill
fix(gui): RSGraphWidget area fill — close polygon on a flat baseline
2026-06-24 09:25:20 +02:00
csoler
d788d23204
Merge pull request #3231 from jolavillette/fix/macos-camera-mic-permissions
fix(macos): add camera & microphone usage descriptions to Info.plist
2026-06-23 18:14:38 +02:00
jolavillette
0c0ad41f77 fix(gui): replace QDateTime::setTime_t with setSecsSinceEpoch for Qt6
QDateTime::setTime_t() was deprecated in Qt 5.8 and removed in Qt6.
setSecsSinceEpoch() exists since Qt 5.8 so the GUI keeps building
against both Qt5 and Qt6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 18:14:53 +02:00