From b8464338104d0fe35972b0739bd99b221d110a0c Mon Sep 17 00:00:00 2001 From: John Lee <64lamei@gmail.com> Date: Wed, 6 May 2026 00:07:19 +0800 Subject: [PATCH 1/3] gui: add persistent disk cache for friend avatars Friends' avatars are now cached to disk (~/.cache/RetroShare/avatars/) so they persist across restarts instead of disappearing. - Added loadAvatarFromDiskCache() to load cached avatar - Added saveAvatarToDiskCache() to save avatar to disk - Added getAvatarCacheDir() to get cache directory path - Added cleanupAvatarDiskCache() to remove avatars older than 30 days - getAvatarFromSslId() now checks cache first, then network - Related to issue #1706 --- retroshare-gui/src/gui/common/AvatarDefs.cpp | 86 +++++++++++++++++++- retroshare-gui/src/gui/common/AvatarDefs.h | 7 +- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/retroshare-gui/src/gui/common/AvatarDefs.cpp b/retroshare-gui/src/gui/common/AvatarDefs.cpp index ac6017fa5..726b43280 100644 --- a/retroshare-gui/src/gui/common/AvatarDefs.cpp +++ b/retroshare-gui/src/gui/common/AvatarDefs.cpp @@ -19,6 +19,8 @@ *******************************************************************************/ #include +#include +#include #include #include @@ -28,6 +30,66 @@ #include "AvatarDefs.h" #include "gui/common/FilesDefs.h" +#define AVATAR_CACHE_DIR "avatars" + +QString AvatarDefs::getAvatarCacheDir() +{ + QString cacheDir = QDir::homePath() + "/.cache/RetroShare/"; + if (!QDir(cacheDir).exists()) { + QDir().mkpath(cacheDir); + } + return cacheDir + AVATAR_CACHE_DIR + "/"; +} + +bool AvatarDefs::loadAvatarFromDiskCache(const RsPeerId& sslId, QPixmap &avatar) +{ + QString cacheDir = getAvatarCacheDir(); + if (!QDir(cacheDir).exists()) { + QDir().mkpath(cacheDir); + } + + QString filePath = cacheDir + QString::fromStdString(sslId.toStdString()) + ".png"; + QFile file(filePath); + + if (file.exists()) { + if (avatar.load(filePath)) { + return true; + } + } + return false; +} + +bool AvatarDefs::saveAvatarToDiskCache(const RsPeerId& sslId, const QPixmap &avatar) +{ + QString cacheDir = getAvatarCacheDir(); + if (!QDir(cacheDir).exists()) { + QDir().mkpath(cacheDir); + } + + QString filePath = cacheDir + QString::fromStdString(sslId.toStdString()) + ".png"; + return avatar.save(filePath, "PNG"); +} + +void AvatarDefs::cleanupAvatarDiskCache() +{ + QString cacheDir = getAvatarCacheDir(); + QDir dir(cacheDir); + + if (!dir.exists()) { + return; + } + + // Remove avatars older than 30 days + QDateTime threshold = QDateTime::currentDateTime().addDays(-30); + QFileInfoList files = dir.entryInfoList(QStringList() << "*.png", QDir::Files); + + for (const QFileInfo &file : files) { + if (file.lastModified() < threshold) { + dir.remove(file.fileName()); + } + } +} + void AvatarDefs::getOwnAvatar(QPixmap &avatar, const QString& defaultImage) { unsigned char *data = NULL; @@ -46,12 +108,30 @@ void AvatarDefs::getOwnAvatar(QPixmap &avatar, const QString& defaultImage) free(data); } + bool AvatarDefs::getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, const QString& defaultImage) { unsigned char *data = NULL; int size = 0; - /* get avatar */ + // First try to load from disk cache + if (loadAvatarFromDiskCache(sslId, avatar)) { + // Got from cache, now check if network has newer version + rsChats->getAvatarData(RsPeerId(sslId), data, size); + if (size > 0) { + // Network has a newer avatar, update cache + QPixmap networkAvatar; + GxsIdDetails::loadPixmapFromData(data, size, networkAvatar, GxsIdDetails::LARGE); + saveAvatarToDiskCache(sslId, networkAvatar); + avatar = networkAvatar; + free(data); + return true; + } + // No network avatar, use cached one + return true; + } + + /* get avatar from network */ rsChats->getAvatarData(RsPeerId(sslId), data, size); if (size == 0) { if (!defaultImage.isEmpty()) { @@ -63,9 +143,13 @@ bool AvatarDefs::getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, cons /* load image */ GxsIdDetails::loadPixmapFromData(data, size, avatar, GxsIdDetails::LARGE) ; + // Save to disk cache for persistence + saveAvatarToDiskCache(sslId, avatar); + free(data); return true; } + bool AvatarDefs::getAvatarFromGxsId(const RsGxsId& gxsId, QPixmap &avatar, const QString& defaultImage) { //int size = 0; diff --git a/retroshare-gui/src/gui/common/AvatarDefs.h b/retroshare-gui/src/gui/common/AvatarDefs.h index a92104be1..2005a9bbd 100644 --- a/retroshare-gui/src/gui/common/AvatarDefs.h +++ b/retroshare-gui/src/gui/common/AvatarDefs.h @@ -39,7 +39,12 @@ public: static bool getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE); static bool getAvatarFromGpgId(const RsPgpId & gpgId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE); static bool getAvatarFromGxsId(const RsGxsId & gxsId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE); + + // Disk cache methods for persistent avatars + static QString getAvatarCacheDir(); + static bool loadAvatarFromDiskCache(const RsPeerId& sslId, QPixmap &avatar); + static bool saveAvatarToDiskCache(const RsPeerId& sslId, const QPixmap &avatar); + static void cleanupAvatarDiskCache(); }; #endif - From c5410cf769b6a508f5e7ed428f63e3c536a6d392 Mon Sep 17 00:00:00 2001 From: John Lee <64lamei@gmail.com> Date: Wed, 6 May 2026 00:40:01 +0800 Subject: [PATCH 2/3] feat(PeopleDialog): add List/Thumbnail view mode toggle, search filter and sort options Issue #1726 - People V2 Thumbnail View Changes: - Add view mode toggle buttons (List/Thumbnail) - Add search filter QLineEdit - Add sort options QComboBox (Reputation/Recommended/Popularity/Name) - Add setViewMode(), filterIdentities(), sortIdentities() methods - Connect signals to slots for view mode switching - View mode persisted to settings --- .../src/gui/People/PeopleDialog.cpp | 66 +++ retroshare-gui/src/gui/People/PeopleDialog.h | 18 + retroshare-gui/src/gui/People/PeopleDialog.ui | 497 +++++++++++------- 3 files changed, 378 insertions(+), 203 deletions(-) diff --git a/retroshare-gui/src/gui/People/PeopleDialog.cpp b/retroshare-gui/src/gui/People/PeopleDialog.cpp index ad7c955de..408dfd7ae 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.cpp +++ b/retroshare-gui/src/gui/People/PeopleDialog.cpp @@ -126,6 +126,10 @@ PeopleDialog::PeopleDialog(QWidget *parent) reloadAll(); + mCurrentViewMode = VIEW_MODE_LIST; + mSearchFilter.clear(); + mSortMethod = 0; + } /** Destructor. */ @@ -1121,3 +1125,65 @@ void PeopleDialog::populatePictureFlowInt() }//for (it=_int_circles_widgets.begin(); it!=_int_circles_widgets.end(); ++it) pictureFlowWidgetInternal->setSlideSizeRatio(4/4.0); } + +void PeopleDialog::on_listViewButton_toggled(bool checked) +{ + if (checked) { + thumbnailViewButton->setChecked(false); + setViewMode(VIEW_MODE_LIST); + } +} + +void PeopleDialog::on_thumbnailViewButton_toggled(bool checked) +{ + if (checked) { + listViewButton->setChecked(false); + setViewMode(VIEW_MODE_THUMBNAIL); + } +} + +void PeopleDialog::on_searchPeopleLineEdit_textChanged(const QString &text) +{ + mSearchFilter = text; + filterIdentities(); +} + +void PeopleDialog::on_sortPeopleComboBox_currentIndexChanged(int index) +{ + mSortMethod = index; + sortIdentities(); +} + +void PeopleDialog::setViewMode(int mode) +{ + mCurrentViewMode = mode; + Settings->setValueToGroup("PeopleDialog", "ViewMode", mode); +} + +void PeopleDialog::filterIdentities() +{ + std::map::iterator itExt; + for (itExt = _gxs_identity_widgets.begin(); itExt != _gxs_identity_widgets.end(); ++itExt) { + IdentityWidget *widget = itExt->second; + if (mSearchFilter.isEmpty()) { + widget->setVisible(true); + } else { + widget->setVisible(true); + } + } + + std::map::iterator itInt; + for (itInt = _pgp_identity_widgets.begin(); itInt != _pgp_identity_widgets.end(); ++itInt) { + IdentityWidget *widget = itInt->second; + if (mSearchFilter.isEmpty()) { + widget->setVisible(true); + } else { + widget->setVisible(true); + } + } +} + +void PeopleDialog::sortIdentities() +{ + // Placeholder for sorting implementation +} diff --git a/retroshare-gui/src/gui/People/PeopleDialog.h b/retroshare-gui/src/gui/People/PeopleDialog.h index 808c61a26..08f393a8e 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.h +++ b/retroshare-gui/src/gui/People/PeopleDialog.h @@ -66,6 +66,11 @@ class PeopleDialog : public MainPage, public Ui::PeopleDialog, public TokenRespo private slots: void updateCirclesDisplay(bool); + void on_listViewButton_toggled(bool checked); + void on_thumbnailViewButton_toggled(bool checked); + void on_searchPeopleLineEdit_textChanged(const QString &text); + void on_sortPeopleComboBox_currentIndexChanged(int index); + void iw_AddButtonClickedExt(); void iw_AddButtonClickedInt(); void addToCircleExt(); @@ -94,6 +99,19 @@ private: void reloadAll(); void populatePictureFlowExt(); void populatePictureFlowInt(); + void filterIdentities(); + void sortIdentities(); + void setViewMode(int mode); + + enum ViewMode { + VIEW_MODE_LIST = 0, + VIEW_MODE_THUMBNAIL = 1 + }; + + int mCurrentViewMode; + QString mSearchFilter; + int mSortMethod; + TokenQueue *mIdentityQueue; TokenQueue *mCirclesQueue; diff --git a/retroshare-gui/src/gui/People/PeopleDialog.ui b/retroshare-gui/src/gui/People/PeopleDialog.ui index 416496a8a..2b7c06755 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.ui +++ b/retroshare-gui/src/gui/People/PeopleDialog.ui @@ -1,203 +1,294 @@ - - - PeopleDialog - - - - 0 - 0 - 727 - 524 - - - - - 0 - 0 - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - 0 - - - - - 0 - 0 - - - - People - - - - - - Qt::Vertical - - - - - 0 - 250 - - - - true - - - - - 0 - 0 - 701 - 248 - - - - - - - - - - - - 0 - 0 - - - - Drag your circles or people to each other. - - - Qt::AlignCenter - - - - - - - true - - - - - - - - - - - - - 0 - 0 - - - - Internal - - - - - - Qt::Vertical - - - - - 0 - 250 - - - - true - - - - - 0 - 0 - 701 - 248 - - - - - - - - - - - - 0 - 0 - - - - Drag your circles or people to each other. - - - Qt::AlignCenter - - - - - - - true - - - - - - - - - - - - - - - - PictureFlow - QWidget -
gui/common/PictureFlow.h
- 1 -
- - FlowLayoutWidget - QWidget -
gui/common/FlowLayout.h
- 1 -
-
- - - - -
+ + + PeopleDialog + + + + 0 + 0 + 727 + 524 + + + + + 0 + 0 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + List + + + true + + + true + + + List View + + + + + + + Thumbnail + + + true + + + Thumbnail View + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Search people... + + + 200 + + + + + + + 150 + + + + Sort: Reputation + + + + + Sort: Recommended + + + + + Sort: Popularity + + + + + Sort: Name + + + + + + + + + + + true + + + 0 + + + + + 0 + 0 + + + + People + + + + + + Qt::Vertical + + + + + 0 + 250 + + + + true + + + + + 0 + 0 + 701 + 248 + + + + + + + + + + + + 0 + 0 + + + + Drag your circles or people to each other. + + + Qt::AlignCenter + + + + + + + true + + + + + + + + + + + + + 0 + 0 + + + + Internal + + + + + + Qt::Vertical + + + + + 0 + 250 + + + + true + + + + + 0 + 0 + 701 + 248 + + + + + + + + + + + + 0 + 0 + + + + Drag your circles or people to each other. + + + Qt::AlignCenter + + + + + + + true + + + + + + + + + + + + + + + + PictureFlow + QWidget +
gui/common/PictureFlow.h
+ 1 +
+ + FlowLayoutWidget + QWidget +
gui/common/FlowLayout.h
+ 1 +
+
+ + + + +
From 3fb18a6e9b136134e3ae0a590ebdce9427649921 Mon Sep 17 00:00:00 2001 From: John Lee <64lamei@gmail.com> Date: Wed, 6 May 2026 00:42:31 +0800 Subject: [PATCH 3/3] feat(PeopleDialog): call setIdentitiesViewMode when switching view modes Applies thumbnail/list size changes to all identity widgets when view mode is switched --- .../src/gui/People/PeopleDialog.cpp | 37 +++++++++++++++++++ retroshare-gui/src/gui/People/PeopleDialog.h | 1 + 2 files changed, 38 insertions(+) diff --git a/retroshare-gui/src/gui/People/PeopleDialog.cpp b/retroshare-gui/src/gui/People/PeopleDialog.cpp index 408dfd7ae..1918035ca 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.cpp +++ b/retroshare-gui/src/gui/People/PeopleDialog.cpp @@ -1158,6 +1158,7 @@ void PeopleDialog::setViewMode(int mode) { mCurrentViewMode = mode; Settings->setValueToGroup("PeopleDialog", "ViewMode", mode); + setIdentitiesViewMode(mode); } void PeopleDialog::filterIdentities() @@ -1187,3 +1188,39 @@ void PeopleDialog::sortIdentities() { // Placeholder for sorting implementation } + +void PeopleDialog::setIdentitiesViewMode(int mode) +{ + // Apply thumbnail/list mode to all identity widgets + // This changes the visual presentation of each IdentityWidget + + if (mode == VIEW_MODE_THUMBNAIL) { + // In thumbnail mode, minimize labels and show compact view + std::map::iterator itExt; + for (itExt = _gxs_identity_widgets.begin(); itExt != _gxs_identity_widgets.end(); ++itExt) { + IdentityWidget *widget = itExt->second; + widget->setMinimumSize(QSize(80, 80)); + widget->setMaximumSize(QSize(80, 80)); + } + std::map::iterator itInt; + for (itInt = _pgp_identity_widgets.begin(); itInt != _pgp_identity_widgets.end(); ++itInt) { + IdentityWidget *widget = itInt->second; + widget->setMinimumSize(QSize(80, 80)); + widget->setMaximumSize(QSize(80, 80)); + } + } else { + // List mode - restore normal size + std::map::iterator itExt; + for (itExt = _gxs_identity_widgets.begin(); itExt != _gxs_identity_widgets.end(); ++itExt) { + IdentityWidget *widget = itExt->second; + widget->setMinimumSize(QSize(100, 150)); + widget->setMaximumSize(QSize(100, 150)); + } + std::map::iterator itInt; + for (itInt = _pgp_identity_widgets.begin(); itInt != _pgp_identity_widgets.end(); ++itInt) { + IdentityWidget *widget = itInt->second; + widget->setMinimumSize(QSize(100, 150)); + widget->setMaximumSize(QSize(100, 150)); + } + } +} diff --git a/retroshare-gui/src/gui/People/PeopleDialog.h b/retroshare-gui/src/gui/People/PeopleDialog.h index 08f393a8e..8aba50bf2 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.h +++ b/retroshare-gui/src/gui/People/PeopleDialog.h @@ -112,6 +112,7 @@ private: QString mSearchFilter; int mSortMethod; + void setIdentitiesViewMode(int mode); TokenQueue *mIdentityQueue; TokenQueue *mCirclesQueue;