diff --git a/retroshare-gui/src/CMakeLists.txt b/retroshare-gui/src/CMakeLists.txt index dc70d7c09..71dd6770f 100644 --- a/retroshare-gui/src/CMakeLists.txt +++ b/retroshare-gui/src/CMakeLists.txt @@ -999,7 +999,10 @@ if(RS_GXSTHEWIRE) src/gui/TheWire/PulseViewGroup.h src/gui/TheWire/PulseReply.h src/gui/TheWire/PulseReplySeperator.h - src/gui/TheWire/PulseMessage.h + src/gui/TheWire/PulseMessage.h + src/gui/TheWire/WireUserNotify.h + src/gui/feeds/WireNotifyGroupItem.h + src/gui/feeds/WireNotifyPostItem.h ) list( @@ -1012,7 +1015,9 @@ if(RS_GXSTHEWIRE) src/gui/TheWire/PulseViewGroup.ui src/gui/TheWire/PulseReply.ui src/gui/TheWire/PulseReplySeperator.ui - src/gui/TheWire/PulseMessage.ui + src/gui/TheWire/PulseMessage.ui + src/gui/feeds/WireNotifyGroupItem.ui + src/gui/feeds/WireNotifyPostItem.ui ) list( @@ -1028,6 +1033,9 @@ if(RS_GXSTHEWIRE) src/gui/TheWire/PulseReply.cpp src/gui/TheWire/PulseReplySeperator.cpp src/gui/TheWire/PulseMessage.cpp + src/gui/TheWire/WireUserNotify.cpp + src/gui/feeds/WireNotifyGroupItem.cpp + src/gui/feeds/WireNotifyPostItem.cpp ) list( @@ -1112,6 +1120,7 @@ if(RS_GXSGUI) src/gui/gxs/GxsFeedWidget.h src/util/TokenQueue.h src/util/RsGxsUpdateBroadcast.h + src/gui/gxs/GxsStatisticsProvider.h ) list( @@ -1147,6 +1156,7 @@ if(RS_GXSGUI) src/gui/gxs/GxsFeedWidget.cpp src/util/TokenQueue.cpp src/util/RsGxsUpdateBroadcast.cpp + src/gui/gxs/GxsStatisticsProvider.cpp ) endif(RS_GXSGUI) diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index cbbbff8b2..8c50230cf 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -463,7 +463,6 @@ void MainWindow::initStackedPage() #endif #ifdef RS_USE_WIRE - WireDialog *wireDialog = NULL; addPage(wireDialog = new WireDialog(ui->stackPages), grp, ¬ify); #endif @@ -1081,6 +1080,11 @@ void SetForegroundWindowInternal(HWND hWnd) case Posted: _instance->ui->stackPages->setCurrentPage( _instance->postedDialog ); return true ; +#ifdef RS_USE_WIRE + case Wire: + _instance->ui->stackPages->setCurrentPage( _instance->wireDialog ); + return true ; +#endif default: std::cerr << "Show page called on value that is not handled yet. Please code it! (value = " << page << ")" << std::endl; } @@ -1118,6 +1122,11 @@ void SetForegroundWindowInternal(HWND hWnd) if (page == _instance->messagesDialog) { return Messages; } +#ifdef RS_USE_WIRE + if (page == _instance->wireDialog) { + return Wire; + } +#endif #if 0 if (page == _instance->channelFeed) { return Channels; @@ -1157,6 +1166,7 @@ void SetForegroundWindowInternal(HWND hWnd) return _instance->transfersDialog->searchDialog; case Messages: return _instance->messagesDialog; + case Channels: return _instance->gxschannelDialog; case Forums: @@ -1165,6 +1175,10 @@ void SetForegroundWindowInternal(HWND hWnd) return _instance->postedDialog; case Home: return _instance->homePage; +#ifdef RS_USE_WIRE + case Wire: + return _instance->wireDialog; +#endif } return NULL; diff --git a/retroshare-gui/src/gui/MainWindow.h b/retroshare-gui/src/gui/MainWindow.h index 0fbdcec46..bec08c5ed 100644 --- a/retroshare-gui/src/gui/MainWindow.h +++ b/retroshare-gui/src/gui/MainWindow.h @@ -71,7 +71,9 @@ class BandwidthGraph; class MainPage; class NewsFeed; class UserNotify; - +#ifdef RS_USE_WIRE +class WireDialog; +#endif #ifdef MESSENGER_WINDOW class MessengerWindow; #endif @@ -107,7 +109,10 @@ public: Posted = 11, /** Posted links */ People = 12, /** People page. */ Options = 13, /** People page. */ - Home = 14 /** Home page. */ + Home = 14, /** Home page. */ +#ifdef RS_USE_WIRE + Wire = 15 /** Wire page. */ +#endif }; @@ -167,7 +172,9 @@ public: GxsChannelDialog *gxschannelDialog ; GxsForumsDialog *gxsforumDialog ; PostedDialog *postedDialog; - +#ifdef RS_USE_WIRE + WireDialog *wireDialog; +#endif // ForumsDialog *forumsDialog; // ChannelFeed *channelFeed; Idle *idle; diff --git a/retroshare-gui/src/gui/NewsFeed.cpp b/retroshare-gui/src/gui/NewsFeed.cpp index cf901d1e7..069548356 100644 --- a/retroshare-gui/src/gui/NewsFeed.cpp +++ b/retroshare-gui/src/gui/NewsFeed.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "util/misc.h" #include "util/qtthreadsutils.h" @@ -49,6 +50,8 @@ #include "feeds/PostedGroupItem.h" #include "feeds/SecurityItem.h" #include "feeds/SecurityIpItem.h" +#include "feeds/WireNotifyGroupItem.h" +#include "feeds/WireNotifyPostItem.h" #include "settings/rsettingswin.h" #include "settings/rsharesettings.h" @@ -83,7 +86,8 @@ NewsFeed::NewsFeed(QWidget *parent) : MainPage(parent), ui(new Ui::NewsFeed), RsEventType::GXS_CHANNELS , RsEventType::GXS_FORUMS , RsEventType::GXS_POSTED , - RsEventType::MAIL_STATUS + RsEventType::MAIL_STATUS , + RsEventType::WIRE }) { /* Invoke the Qt Designer generated object setup routine */ @@ -129,6 +133,9 @@ NewsFeed::NewsFeed(QWidget *parent) : MainPage(parent), ui(new Ui::NewsFeed), "
  • Channel and Board comments
  • " "
  • New Mail messages
  • " "
  • Private messages from your friends
  • " +#ifdef RS_USE_WIRE + "
  • New Wire pulses and mentions
  • " +#endif "

    " ).arg(QString::number(2*H)); @@ -216,6 +223,10 @@ void NewsFeed::handleEvent_main_thread(std::shared_ptr event) if(event->mType == RsEventType::MAIL_STATUS && (!!(flags & RsFeedTypeFlags::RS_FEED_TYPE_MSG))) handleMailEvent(event); + + if(event->mType == RsEventType::WIRE && (!!(flags & RsFeedTypeFlags::RS_FEED_TYPE_WIRE))) + handleWireEvent(event); + } void NewsFeed::handleMailEvent(std::shared_ptr event) @@ -503,6 +514,35 @@ void NewsFeed::handleSecurityEvent(std::shared_ptr event) MessageComposer::addConnectAttemptMsg(e.mPgpId, e.mSslId, QString::fromStdString(det.name + "(" + det.location + ")")); } +void NewsFeed::handleWireEvent(std::shared_ptr event) +{ + const RsWireEvent *pe = + dynamic_cast(event.get()); + if(!pe) return; + + switch(pe->mWireEventCode) + { + case RsWireEventCode::FOLLOW_STATUS_CHANGED: + addFeedItem( new WireNotifyGroupItem(this, NEWSFEED_WIRELIST, pe->mWireGroupId, false, true, RsWireEventCode::FOLLOW_STATUS_CHANGED)); + break; + case RsWireEventCode::NEW_WIRE: + addFeedItem( new WireNotifyGroupItem(this, NEWSFEED_WIRELIST, pe->mWireGroupId, false, true, RsWireEventCode::NEW_WIRE)); + break; + case RsWireEventCode::NEW_POST: + addFeedItem( new WireNotifyPostItem(this, NEWSFEED_WIRELIST, pe->mWireGroupId, pe->mWireMsgId, true)); + break; + case RsWireEventCode::WIRE_UPDATED: + addFeedItem( new WireNotifyGroupItem(this, NEWSFEED_WIRELIST, pe->mWireGroupId, false, true, RsWireEventCode::WIRE_UPDATED)); + break; +// case RsWireEventCode::POST_UPDATED: +// addFeedItem( new WireNotifyGroupItem(this, NEWSFEED_WIRELIST, pe->mWireGroupId, false, true)); +// break; + default: break; + } + sendNewsFeedChanged(); +} + + void NewsFeed::testFeeds(RsFeedTypeFlags /*notifyFlags*/) { RsFeedTypeFlags flags = RsFeedTypeFlags(Settings->getNewsFeedFlags()); diff --git a/retroshare-gui/src/gui/NewsFeed.h b/retroshare-gui/src/gui/NewsFeed.h index fed1d41b5..876eef95a 100644 --- a/retroshare-gui/src/gui/NewsFeed.h +++ b/retroshare-gui/src/gui/NewsFeed.h @@ -49,6 +49,7 @@ const uint32_t NEWSFEED_POSTEDNEWLIST = 0x000b; const uint32_t NEWSFEED_POSTEDMSGLIST = 0x000c; const uint32_t NEWSFEED_CIRCLELIST = 0x000d; const uint32_t NEWSFEED_CHANNELPUBKEYLIST= 0x000e; +const uint32_t NEWSFEED_WIRELIST = 0x0010; namespace Ui { class NewsFeed; @@ -107,6 +108,7 @@ private: void handleMailEvent(std::shared_ptr event); void handlePostedEvent(std::shared_ptr event); void handleChannelEvent(std::shared_ptr event); + void handleWireEvent(std::shared_ptr event); void addFeedItem(FeedItem *item); void addFeedItemIfUnique(FeedItem *item, bool replace); diff --git a/retroshare-gui/src/gui/Posted/PostedDialog.h b/retroshare-gui/src/gui/Posted/PostedDialog.h index 9f764dff6..97fc8e648 100644 --- a/retroshare-gui/src/gui/Posted/PostedDialog.h +++ b/retroshare-gui/src/gui/Posted/PostedDialog.h @@ -54,7 +54,7 @@ private: /* GxsGroupFrameDialog */ virtual QString text(TextType type) override; virtual QString icon(IconType type) override; - virtual QString settingsGroupName() override{ return "PostedDialog"; } + virtual QString settingsGroupName() { return "PostedDialog"; } virtual GxsGroupDialog *createNewGroupDialog() override; virtual GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) override; virtual int shareKeyType() override; diff --git a/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp b/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp index ce6e49835..305bc6fac 100644 --- a/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp +++ b/retroshare-gui/src/gui/Posted/PostedUserNotify.cpp @@ -23,7 +23,7 @@ #include "gui/MainWindow.h" #include "gui/common/FilesDefs.h" -PostedUserNotify::PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent) : +PostedUserNotify::PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent) : GxsUserNotify(ifaceImpl, g, parent) { } diff --git a/retroshare-gui/src/gui/Posted/PostedUserNotify.h b/retroshare-gui/src/gui/Posted/PostedUserNotify.h index b7631bc37..26bb08e22 100644 --- a/retroshare-gui/src/gui/Posted/PostedUserNotify.h +++ b/retroshare-gui/src/gui/Posted/PostedUserNotify.h @@ -28,7 +28,7 @@ class PostedUserNotify : public GxsUserNotify Q_OBJECT public: - explicit PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); + explicit PostedUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group) override; diff --git a/retroshare-gui/src/gui/RetroShareLink.cpp b/retroshare-gui/src/gui/RetroShareLink.cpp index 5f9ba9339..66f9397aa 100644 --- a/retroshare-gui/src/gui/RetroShareLink.cpp +++ b/retroshare-gui/src/gui/RetroShareLink.cpp @@ -33,6 +33,7 @@ #include "FileTransfer/SearchDialog.h" #include "gxschannels/GxsChannelDialog.h" #include "gxsforums/GxsForumsDialog.h" +#include "TheWire/WireDialog.h" #include "msgs/MessageComposer.h" #include "Posted/PostedDialog.h" #include "util/misc.h" @@ -74,7 +75,8 @@ #define HOST_IDENTITY "identity" #define HOST_FILE_TREE "collection" #define HOST_CHAT_ROOM "chat_room" -#define HOST_REGEXP "file|person|forum|channel|search|message|certificate|extra|private_chat|public_msg|posted|identity|collection|chat_room" +#define HOST_REGEXP "file|person|forum|channel|wire|search|message|certificate|extra|private_chat|public_msg|posted|identity|collection|chat_room" +#define HOST_WIRE "wire" #define FILE_NAME "name" #define FILE_SIZE "size" @@ -92,6 +94,10 @@ #define CHANNEL_ID "id" #define CHANNEL_MSGID "msgid" +#define WIRE_NAME "name" +#define WIRE_ID "id" +#define WIRE_MSGID "msgid" + #define SEARCH_KEYWORDS "keywords" #define MESSAGE_ID "id" @@ -289,6 +295,19 @@ void RetroShareLink::fromUrl(const QUrl& url) return; } + if (url.host() == HOST_WIRE) { + _type = TYPE_WIRE; + _name = decodedQueryItemValue(urlQuery, WIRE_NAME); + _hash = urlQuery.queryItemValue(WIRE_ID); + _msgId = urlQuery.queryItemValue(WIRE_MSGID); + +#ifdef DEBUG_RSLINK + std::cerr << "Got a wire link!!" << std::endl; +#endif + check(); + return; + } + if (url.host() == HOST_SEARCH) { _type = TYPE_SEARCH; _name = decodedQueryItemValue(urlQuery, SEARCH_KEYWORDS); @@ -468,7 +487,7 @@ RetroShareLink RetroShareLink::createPerson(const RsPgpId& id) return link; } -//For Forum, Channel & Posted +//For Forum, Channel, Posted and Wire RetroShareLink RetroShareLink::createGxsGroupLink(const RetroShareLink::enumType &linkType, const RsGxsGroupId &groupId, const QString &groupName) { RetroShareLink link; @@ -793,6 +812,17 @@ void RetroShareLink::check() _valid = false; break; + case TYPE_WIRE: + if(_size != 0) + _valid = false; + + if(_name.isEmpty()) + _valid = false; + + if(_hash.isEmpty()) + _valid = false; + break; + case TYPE_SEARCH: if(_size != 0) _valid = false; @@ -886,6 +916,8 @@ QString RetroShareLink::title() const return QString("Forum id: %1").arg(hash()); case TYPE_CHANNEL: return QString("Channel id: %1").arg(hash()); + case TYPE_WIRE: + return QString("Wire id: %1").arg(hash()); case TYPE_SEARCH: return QString("Search files"); @@ -1073,6 +1105,17 @@ QString RetroShareLink::toString() const break; + case TYPE_WIRE: + url.setScheme(RSLINK_SCHEME); + url.setHost(HOST_WIRE); + urlQuery.addQueryItem(WIRE_NAME, encodeItem(_name)); + urlQuery.addQueryItem(WIRE_ID, _hash); + if (!_msgId.isEmpty()) { + urlQuery.addQueryItem(WIRE_MSGID, _msgId); + } + + break; + } #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) @@ -1307,6 +1350,7 @@ static void processList(const QStringList &list, const QString &textSingular, co case TYPE_FILE: case TYPE_FORUM: case TYPE_CHANNEL: + case TYPE_WIRE: case TYPE_SEARCH: case TYPE_MESSAGE: case TYPE_CERTIFICATE: @@ -1355,6 +1399,12 @@ static void processList(const QStringList &list, const QString &textSingular, co QStringList channelUnknown; QStringList channelMsgUnknown; + // wire + QStringList wireFound; + QStringList wireMsgFound; + QStringList wireUnknown; + QStringList wireMsgUnknown; + // search QStringList searchStarted; @@ -1381,9 +1431,9 @@ static void processList(const QStringList &list, const QString &textSingular, co QList processedList; QList errorList; - processedList << &fileAdded << &personAdded << &forumFound << &channelFound << &searchStarted << &messageStarted << &postedFound << &chatroomFound; - errorList << &fileExist << &personExist << &personFailed << &personNotFound << &forumUnknown << &forumMsgUnknown << &channelUnknown << &channelMsgUnknown << &messageReceipientNotAccepted << &messageReceipientUnknown << &postedUnknown << &postedMsgUnknown << &chatroomUnknown; - // not needed: forumFound, channelFound, messageStarted + processedList << &fileAdded << &personAdded << &forumFound << &channelFound << &wireFound << &searchStarted << &messageStarted << &postedFound << &chatroomFound; + errorList << &fileExist << &personExist << &personFailed << &personNotFound << &forumUnknown << &forumMsgUnknown << &channelUnknown << &channelMsgUnknown << &wireUnknown << &wireMsgUnknown << &messageReceipientNotAccepted << &messageReceipientUnknown << &postedUnknown << &postedMsgUnknown << &chatroomUnknown; + // not needed: forumFound, channelFound, wireFound, messageStarted // we want to merge all single file links into one collection // if a collection tree link is found it is processed independen for the other file links @@ -1475,6 +1525,34 @@ static void processList(const QStringList &list, const QString &textSingular, co } break; + case TYPE_WIRE: + { + #ifdef DEBUG_RSLINK + std::cerr << " RetroShareLink::process WireRequest : name : " << link.name().toStdString() << ". id : " << link.hash().toStdString() << ". msgId : " << link.msgId().toStdString() << std::endl; + #endif + + MainWindow::showWindow(MainWindow::Wire); + WireDialog *wireDialog = dynamic_cast(MainWindow::getPage(MainWindow::Wire)); + if (!wireDialog) { + return false; + } + + if (wireDialog->navigate(RsGxsGroupId(link.id().toStdString()), RsGxsMessageId(link.msgId().toStdString()))) { + if (link.msgId().isEmpty()) { + wireFound.append(link.name()); + } else { + wireMsgFound.append(link.name()); + } + } else { + if (link.msgId().isEmpty()) { + wireUnknown.append(link.name()); + } else { + wireMsgUnknown.append(link.name()); + } + } + } + break; + case TYPE_SEARCH: { #ifdef DEBUG_RSLINK @@ -1764,7 +1842,7 @@ static void processList(const QStringList &list, const QString &textSingular, co } else { - ChatDialog* chatDialog = ChatDialog::getChat(chatId, (RsChatFlags)Settings->getChatFlags()); + ChatDialog* chatDialog = ChatDialog::getChat(chatId, (RsChatFlags)Settings->getChatFlags()); if (chatDialog) { chatroomFound.append(link.name()); } else { @@ -1861,6 +1939,16 @@ static void processList(const QStringList &list, const QString &textSingular, co } } + // wire + if (flag & RSLINK_PROCESS_NOTIFY_ERROR) { + if (!wireUnknown.isEmpty()) { + processList(wireUnknown, QObject::tr("Wire not found"), QObject::tr("Wires not found"), result); + } + if (!wireMsgUnknown.isEmpty()) { + processList(wireMsgUnknown, QObject::tr("Wire message not found"), QObject::tr("Wire messages not found"), result); + } + } + // message if (flag & RSLINK_PROCESS_NOTIFY_ERROR) { if (!messageReceipientNotAccepted.isEmpty()) { @@ -2044,3 +2132,4 @@ void RSLinkClipboard::parseText(QString text, QList &links,Retro pos += rx.matchedLength(); } } + diff --git a/retroshare-gui/src/gui/RetroShareLink.h b/retroshare-gui/src/gui/RetroShareLink.h index 2225392fe..d3cdfda16 100644 --- a/retroshare-gui/src/gui/RetroShareLink.h +++ b/retroshare-gui/src/gui/RetroShareLink.h @@ -75,7 +75,8 @@ class RetroShareLink TYPE_POSTED = 0x0b, TYPE_IDENTITY = 0x0c, TYPE_FILE_TREE = 0x0d, - TYPE_CHAT_ROOM = 0x0e + TYPE_CHAT_ROOM = 0x0e, + TYPE_WIRE = 0x0f }; public: diff --git a/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp b/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp index d3b666a7e..6e681329a 100644 --- a/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp +++ b/retroshare-gui/src/gui/TheWire/PulseAddDialog.cpp @@ -54,7 +54,7 @@ PulseAddDialog::PulseAddDialog(QWidget *parent) ui.pushButton_remove->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/mail/delete.png"))); ui.frame_picture->hide(); - ui.pushButton_picture->hide(); + //ui.pushButton_picture->hide(); // initially hiding the browse button as the attach image button is not pressed //ui.frame_PictureBrowse->hide(); @@ -135,29 +135,35 @@ void PulseAddDialog::cleanup() std::cerr << "PulseAddDialog::cleanup() cleaning up old replyto"; std::cerr << std::endl; QLayout *layout = ui.widget_replyto->layout(); - // completely delete layout and sublayouts - QLayoutItem * item; - while ((item = layout->takeAt(0))) + if (layout) { - if (QWidget *widget = item->widget()) + // completely delete layout and sublayouts + QLayoutItem * item; + while ((item = layout->takeAt(0))) { - std::cerr << "PulseAddDialog::cleanup() removing widget"; - std::cerr << std::endl; - widget->hide(); - delete widget; - } - else - { - std::cerr << "PulseAddDialog::cleanup() removing item"; - std::cerr << std::endl; - delete item; + if (QWidget *widget = item->widget()) + { + std::cerr << "PulseAddDialog::cleanup() removing widget"; + std::cerr << std::endl; + widget->hide(); + delete widget; + } + else + { + std::cerr << "PulseAddDialog::cleanup() removing item"; + std::cerr << std::endl; + delete item; + } } + // then finally + delete layout; } - // then finally - delete layout; mIsReply = false; } + mReplyToPulse = RsWirePulse(); + mReplyType = 0; + ui.frame_reply->setVisible(false); ui.comboBox_sentiment->setCurrentIndex(0); ui.lineEdit_URL->setText(""); @@ -260,6 +266,13 @@ void PulseAddDialog::setReplyTo(const RsGxsGroupId &grpId, const RsGxsMessageId if(grpId.isNull()){ return; } + + mIsReply = true; + mReplyType = replyType; + ui.postButton->setEnabled(false); + ui.postButton->setText(tr("Loading...")); + show(); + /* fetch in the background */ RsThread::async([this,grpId,msgId,replyType](){ @@ -269,12 +282,14 @@ void PulseAddDialog::setReplyTo(const RsGxsGroupId &grpId, const RsGxsMessageId if(!rsWire->getWireGroup(grpId,pGroup)) { std::cerr << __PRETTY_FUNCTION__ << "PulseAddDialog::setRplyTo() failed to fetch group id: " << grpId << std::endl; + RsQThreadUtils::postToObject([this]() { hide(); }, this); return; } if (!rsWire->getWirePulse(grpId, msgId, pPulse)) { std::cerr << "PulseAddDialog::setRplyTo() failed to fetch pulse of group id: " << grpId << std::endl; + RsQThreadUtils::postToObject([this]() { hide(); }, this); return; } @@ -292,6 +307,7 @@ void PulseAddDialog::setReplyTo(const RsGxsGroupId &grpId, const RsGxsMessageId * Qt::QueuedConnection is important! */ + mGroup = *pGroup; setReplyTo(*pPulse, pPulse, pGroup->mMeta.mGroupName, replyType); }, this ); @@ -408,6 +424,21 @@ void PulseAddDialog::postReplyPulse() std::cerr << "PulseAddDialog::postReplyPulse()"; std::cerr << std::endl; + if (mReplyToPulse.mMeta.mGroupId.isNull()) { + std::cerr << "PulseAddDialog::postReplyPulse() reply data not ready yet"; + std::cerr << std::endl; + return; + } + + if (mGroup.mMeta.mGroupId.isNull()) { + std::cerr << "PulseAddDialog::postReplyPulse() group not set"; + std::cerr << std::endl; + return; + } + + ui.postButton->setEnabled(false); + setCursor(Qt::WaitCursor); + RsWirePulseSPtr pPulse(new RsWirePulse()); pPulse->mSentiment = toPulseSentiment(ui.comboBox_sentiment->currentIndex()); @@ -440,6 +471,11 @@ void PulseAddDialog::postReplyPulse() { std::cerr << "PulseAddDialog::postReplyPulse() FAILED"; std::cerr << std::endl; + RsQThreadUtils::postToObject([this]() + { + setCursor(Qt::ArrowCursor); + ui.postButton->setEnabled(true); + }, this); return; } @@ -451,6 +487,7 @@ void PulseAddDialog::postReplyPulse() * Qt::QueuedConnection is important! */ + setCursor(Qt::ArrowCursor); clearDialog(); hide(); }, this ); diff --git a/retroshare-gui/src/gui/TheWire/PulseAddDialog.h b/retroshare-gui/src/gui/TheWire/PulseAddDialog.h index b1e8eca47..8be4b6462 100644 --- a/retroshare-gui/src/gui/TheWire/PulseAddDialog.h +++ b/retroshare-gui/src/gui/TheWire/PulseAddDialog.h @@ -44,6 +44,7 @@ public: void setReplyTo(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId, uint32_t replyType); void setGroup(const RsGxsGroupId &grpId); + void setGroup(RsWireGroup &group); private slots: void addURL(); @@ -57,8 +58,6 @@ private slots: void removePictures(); private: - // OLD VERSIONs, private now. - void setGroup(RsWireGroup &group); void setReplyTo(const RsWirePulse &pulse, RsWirePulseSPtr pPulse, std::string &groupName, uint32_t replyType); void postOriginalPulse(); diff --git a/retroshare-gui/src/gui/TheWire/PulseReply.cpp b/retroshare-gui/src/gui/TheWire/PulseReply.cpp index 5067b5f9b..0ad0c2419 100644 --- a/retroshare-gui/src/gui/TheWire/PulseReply.cpp +++ b/retroshare-gui/src/gui/TheWire/PulseReply.cpp @@ -57,6 +57,8 @@ void PulseReply::setup() connect(toolButton_republish, SIGNAL(clicked()), this, SLOT(actionRepublish())); connect(toolButton_like, SIGNAL(clicked()), this, SLOT(actionLike())); connect(toolButton_view, SIGNAL(clicked()), this, SLOT(actionViewPulse())); + // hided it takes to much space + label_authorName->hide(); } void PulseReply::showReplyLine(bool enable) @@ -150,6 +152,26 @@ void PulseReply::setReferenceString(QString ref) void PulseReply::mousePressEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton && IS_MSG_UNPROCESSED(mPulse->mMeta.mMsgStatus)) + { + uint32_t token; + RsGxsGrpMsgIdPair msgPair = std::make_pair(mPulse->mMeta.mGroupId, mPulse->mMeta.mMsgId); + rsWire->setMessageReadStatus(token, msgPair, true); + } + QWidget::mousePressEvent(event); } +//void WireDialog::setAllMessagesReadDo(bool read, uint32_t &token) +//{ +// if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(subscribeFlags())) { +// return; +// } + +// foreach (RsWirePulseItem *item, mPostItems) { +// RsGxsGrpMsgIdPair msgPair = std::make_pair(item->groupId(), item->messageId()); + +// rsWire->setMessageReadStatus(token, msgPair, read); +// } +//} + diff --git a/retroshare-gui/src/gui/TheWire/PulseReply.ui b/retroshare-gui/src/gui/TheWire/PulseReply.ui index 497d606de..774948559 100644 --- a/retroshare-gui/src/gui/TheWire/PulseReply.ui +++ b/retroshare-gui/src/gui/TheWire/PulseReply.ui @@ -511,7 +511,7 @@ Qt::Horizontal - QSizePolicy::Fixed + QSizePolicy::Expanding @@ -532,21 +532,11 @@ <html><head/><body><p><span style=" color:#2e3436;">· Apr 13 ·</span></p></body></html> + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - - - - Qt::Horizontal - - - - 64 - 18 - - - - diff --git a/retroshare-gui/src/gui/TheWire/PulseTopLevel.cpp b/retroshare-gui/src/gui/TheWire/PulseTopLevel.cpp index 0948e745b..a9270c729 100644 --- a/retroshare-gui/src/gui/TheWire/PulseTopLevel.cpp +++ b/retroshare-gui/src/gui/TheWire/PulseTopLevel.cpp @@ -56,6 +56,7 @@ void PulseTopLevel::setup() connect(toolButton_republish, SIGNAL(clicked()), this, SLOT(actionRepublish())); connect(toolButton_like, SIGNAL(clicked()), this, SLOT(actionLike())); connect(toolButton_view, SIGNAL(clicked()), this, SLOT(actionViewPulse())); + connect(toolButton_copyLink, SIGNAL(clicked()), this, SLOT(actionCopyLink())); } void PulseTopLevel::setRefMessage(QString /*msg*/, uint32_t /*image_count*/) diff --git a/retroshare-gui/src/gui/TheWire/PulseTopLevel.ui b/retroshare-gui/src/gui/TheWire/PulseTopLevel.ui index 1959244d3..ec1c67373 100644 --- a/retroshare-gui/src/gui/TheWire/PulseTopLevel.ui +++ b/retroshare-gui/src/gui/TheWire/PulseTopLevel.ui @@ -731,6 +731,53 @@ + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + COPY LINK + + + + :/images/link.svg:/images/link.svg + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/retroshare-gui/src/gui/TheWire/PulseViewGroup.cpp b/retroshare-gui/src/gui/TheWire/PulseViewGroup.cpp index 475c74034..39739a6d9 100644 --- a/retroshare-gui/src/gui/TheWire/PulseViewGroup.cpp +++ b/retroshare-gui/src/gui/TheWire/PulseViewGroup.cpp @@ -28,6 +28,7 @@ #include "WireGroupDialog.h" #include "gui/gxs/GxsIdDetails.h" +#include "gui/RetroShareLink.h" #include "gui/common/FilesDefs.h" #include "util/DateTime.h" @@ -50,6 +51,7 @@ void PulseViewGroup::setup() { if (mGroup) { connect(followButton, SIGNAL(clicked()), this, SLOT(actionFollow())); + connect(toolButton_copyProfileLink, SIGNAL(clicked()), this, SLOT(actionCopyProfileLink())); label_groupName->setText("@" + QString::fromStdString(mGroup->mMeta.mGroupName)); label_authorName->setText(BoldString(QString::fromStdString(mGroup->mMeta.mAuthorId.toStdString()))); @@ -116,8 +118,14 @@ void PulseViewGroup::setup() label_extra_republishes->setText(BoldString(ToNumberUnits(republishes))); label_extra_likes->setText(BoldString(ToNumberUnits(likes))); - // hide follow. - widget_actions->setVisible(false); + uint32_t following = mGroup->mGroupFollowing; + label_extra_following->setText(BoldString(ToNumberUnits(following))); + + uint32_t followers = mGroup->mGroupFollowers; + label_extra_followers->setText(BoldString(ToNumberUnits(followers))); + + // hide follow button but keep copy link visible. + followButton->setVisible(false); } else { @@ -133,14 +141,17 @@ void PulseViewGroup::setGroupSet() { if (mGroup->mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN) { editButton->show(); + toolButton_copyProfileLink->show(); } else if (mGroup->mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_SUBSCRIBED) { editButton->hide(); + toolButton_copyProfileLink->show(); } else { editButton->hide(); + toolButton_copyProfileLink->show(); } } @@ -156,6 +167,29 @@ void PulseViewGroup::actionFollow() } } +void PulseViewGroup::actionCopyProfileLink() +{ + std::cerr << "PulseViewGroup::actionCopyProfileLink()"; + std::cerr << std::endl; + + if (!mGroup) { + std::cerr << "PulseViewGroup::actionCopyProfileLink() GROUP invalid"; + std::cerr << std::endl; + return; + } + + RetroShareLink link = RetroShareLink::createGxsGroupLink( + RetroShareLink::TYPE_WIRE, + mGroup->mMeta.mGroupId, + QString::fromStdString(mGroup->mMeta.mGroupName)); + + if (link.valid()) { + QList urls; + urls.push_back(link); + RSLinkClipboard::copyLinks(urls); + } +} + void PulseViewGroup::editProfile() { RsGxsGroupId groupId = mGroup->mMeta.mGroupId; diff --git a/retroshare-gui/src/gui/TheWire/PulseViewGroup.h b/retroshare-gui/src/gui/TheWire/PulseViewGroup.h index 62da0a228..4a4b4fd53 100644 --- a/retroshare-gui/src/gui/TheWire/PulseViewGroup.h +++ b/retroshare-gui/src/gui/TheWire/PulseViewGroup.h @@ -35,6 +35,7 @@ public: private slots: void actionFollow(); + void actionCopyProfileLink(); void editProfile(); protected: diff --git a/retroshare-gui/src/gui/TheWire/PulseViewGroup.ui b/retroshare-gui/src/gui/TheWire/PulseViewGroup.ui index 79d9964f6..bb4075bd3 100644 --- a/retroshare-gui/src/gui/TheWire/PulseViewGroup.ui +++ b/retroshare-gui/src/gui/TheWire/PulseViewGroup.ui @@ -25,6 +25,18 @@ + + 0 + + + 0 + + + 0 + + + 0 + @@ -210,6 +222,27 @@ + + + + + 10 + 75 + true + + + + COPY LINK + + + + :/images/link.svg:/images/link.svg + + + Qt::ToolButtonTextBesideIcon + + + @@ -481,6 +514,74 @@ + + + + + + <html><head/><body><p><span style=" font-weight:600;">0</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Following</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">0</span></p></body></html> + + + + + + + <html><head/><body><p><span style=" color:#2e3436;">Followers</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + diff --git a/retroshare-gui/src/gui/TheWire/PulseViewItem.cpp b/retroshare-gui/src/gui/TheWire/PulseViewItem.cpp index b46737bfd..1549d4008 100644 --- a/retroshare-gui/src/gui/TheWire/PulseViewItem.cpp +++ b/retroshare-gui/src/gui/TheWire/PulseViewItem.cpp @@ -25,8 +25,11 @@ #include "PulseViewItem.h" +#include "retroshare/rsgxsflags.h" + #include "gui/gxs/GxsIdDetails.h" #include "gui/common/FilesDefs.h" +#include "gui/RetroShareLink.h" #include "util/DateTime.h" /** Constructor */ @@ -238,6 +241,35 @@ void PulseDataItem::actionRate() } } +void PulseDataItem::actionCopyLink() +{ + std::cerr << "PulseDataItem::actionCopyLink()"; + std::cerr << std::endl; + + if (!mPulse) { + std::cerr << "PulseDataItem::actionCopyLink() PULSE invalid"; + std::cerr << std::endl; + return; + } + + std::string linkName = mPulse->mMeta.mMsgName; + if (linkName.empty()) { + linkName = mPulse->mPulseText.substr(0, 50); + } + + RetroShareLink link = RetroShareLink::createGxsMessageLink( + RetroShareLink::TYPE_WIRE, + mPulse->mMeta.mGroupId, + mPulse->mMeta.mMsgId, + QString::fromStdString(linkName)); + + if (link.valid()) { + QList urls; + urls.push_back(link); + RSLinkClipboard::copyLinks(urls); + } +} + void PulseDataItem::showPulse() @@ -463,3 +495,13 @@ QString ToNumberUnits(uint32_t count) return ans; } +void PulseDataItem::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton && IS_MSG_UNPROCESSED(mPulse->mMeta.mMsgStatus)) + { + uint32_t token; + RsGxsGrpMsgIdPair msgPair = std::make_pair(mPulse->mMeta.mGroupId, mPulse->mMeta.mMsgId); + rsWire->setMessageReadStatus(token, msgPair, true); + } + QWidget::mousePressEvent(event); +} diff --git a/retroshare-gui/src/gui/TheWire/PulseViewItem.h b/retroshare-gui/src/gui/TheWire/PulseViewItem.h index bee96f14b..b057656fe 100644 --- a/retroshare-gui/src/gui/TheWire/PulseViewItem.h +++ b/retroshare-gui/src/gui/TheWire/PulseViewItem.h @@ -114,6 +114,7 @@ private slots: void actionFollow(); void actionFollowParent(); void actionRate(); + void actionCopyLink(); // Action interfaces -------------------------- protected: @@ -137,8 +138,11 @@ protected: // void setReference(uint32_t flags, RsGxsGroupId groupId, std::string groupName); + void mousePressEvent(QMouseEvent *event); + // DATA. RsWirePulseSPtr mPulse; + }; diff --git a/retroshare-gui/src/gui/TheWire/WireDialog.cpp b/retroshare-gui/src/gui/TheWire/WireDialog.cpp index 36a201a7f..10d0c6b4d 100644 --- a/retroshare-gui/src/gui/TheWire/WireDialog.cpp +++ b/retroshare-gui/src/gui/TheWire/WireDialog.cpp @@ -25,20 +25,27 @@ #include "gui/settings/rsharesettings.h" #include "gui/gxs/GxsIdDetails.h" #include "gui/common/FilesDefs.h" +#include "gui/gxs/GxsStatisticsProvider.h" +#include "gui/gxs/GxsGroupShareKey.h" #include "PulseViewGroup.h" #include "PulseReplySeperator.h" +#include "WireUserNotify.h" #include "util/qtthreadsutils.h" +#include "util/misc.h" #include #include +#include #include #include #include #include +#include +#include /**************************************************************** * TheWire Display Widget. @@ -54,19 +61,24 @@ // #define GROUP_SET_RECOMMENDED (5) -#define WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE 1 - - /** Constructor */ WireDialog::WireDialog(QWidget *parent) : MainPage(parent), mGroupSet(GROUP_SET_ALL) - , mAddDialog(nullptr), mGroupSelected(nullptr), mWireQueue(nullptr) - , mHistoryIndex(-1), mEventHandlerId(0) + , mAddDialog(nullptr), mGroupSelected(nullptr) + , mHistoryIndex(-1), mEventHandlerId(0), mSortAscending(false) + , mAccountSortType(0), mHideInactiveAccounts(false) { ui.setupUi(this); + mInterface = rsWire; + mSettingsName = "WireDialog"; + mDistSyncAllowed = true; + mStateHelper = nullptr; + connect( ui.toolButton_createAccount, SIGNAL(clicked()), this, SLOT(createGroup())); connect( ui.toolButton_createPulse, SIGNAL(clicked()), this, SLOT(createPulse())); + connect( ui.toolButton_home, SIGNAL(clicked()), this, SLOT(showHomeFeed())); + connect( ui.lineEdit_searchUsers, SIGNAL(textChanged(const QString&)), this, SLOT(filterUsers(const QString&))); connect(ui.comboBox_groupSet, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGroupSet(int))); connect(ui.comboBox_filterTime, SIGNAL(currentIndexChanged(int)), this, SLOT(selectFilterTime(int))); @@ -76,12 +88,48 @@ WireDialog::WireDialog(QWidget *parent) ui.toolButton_back->setEnabled(false); ui.toolButton_forward->setEnabled(false); + // Setup filter menu + QMenu *filterMenu = new QMenu(this); + QAction *sortAscAction = filterMenu->addAction(tr("Sort by Ascending")); + sortAscAction->setCheckable(true); + sortAscAction->setChecked(mSortAscending); + connect(sortAscAction, SIGNAL(triggered()), this, SLOT(toggleSortAscending())); + + ui.toolButton_filter->setMenu(filterMenu); + + // Setup account filter menu + QMenu *accountMenu = new QMenu(this); + QActionGroup *sortGroup = new QActionGroup(this); + + QAction *sortByName = accountMenu->addAction(tr("Sort by Name")); + sortByName->setCheckable(true); + sortByName->setChecked(true); + sortByName->setData(0); + sortGroup->addAction(sortByName); + + QAction *sortByActivity = accountMenu->addAction(tr("Sort by Activity")); + sortByActivity->setCheckable(true); + sortByActivity->setData(1); + sortGroup->addAction(sortByActivity); + + accountMenu->addSeparator(); + + QAction *hideInactiveAction = accountMenu->addAction(tr("Hide inactive (>30d)")); + hideInactiveAction->setCheckable(true); + hideInactiveAction->setChecked(false); + + connect(sortGroup, SIGNAL(triggered(QAction*)), this, SLOT(sortAccountsChanged(QAction*))); + connect(hideInactiveAction, SIGNAL(toggled(bool)), this, SLOT(hideInactiveChanged(bool))); + + ui.toolButton_accountFilter->setMenu(accountMenu); + QTimer *timer = new QTimer(this); timer->connect(timer, SIGNAL(timeout()), this, SLOT(checkUpdate())); timer->start(1000); - /* setup TokenQueue */ - mWireQueue = new TokenQueue(rsWire->getTokenService(), this); + mCountChildMsgs = false; + mShouldUpdateGroupStatistics = false; + mDistSyncAllowed = true; requestGroupData(); @@ -110,27 +158,33 @@ void WireDialog::handleEvent_main_thread(std::shared_ptr event) #endif // The following switch statements refresh the wire feed whenever there is a new event + switch(e->mWireEventCode) { + + case RsWireEventCode::READ_STATUS_CHANGED: + updateGroupStatisticsReal(e->mWireGroupId); // update the list immediately + return; + + case RsWireEventCode::STATISTICS_CHANGED: case RsWireEventCode::NEW_POST: - case RsWireEventCode::NEW_REPLY: - case RsWireEventCode::NEW_LIKE: - case RsWireEventCode::NEW_REPUBLISH: - case RsWireEventCode::POST_UPDATED: - - case RsWireEventCode::NEW_WIRE: - case RsWireEventCode::FOLLOW_STATUS_CHANGED: + case RsWireEventCode::NEW_WIRE: + updateGroupStatisticsReal(e->mWireGroupId); + break; default: - refreshGroups(); +#ifdef GXSWIRE_DEBUG + RsDbg() << " Unknown event occured: "<< e->mWireEventCode << std::endl; +#endif break; } + refreshGroups(); } } @@ -140,12 +194,148 @@ WireDialog::~WireDialog() processSettings(false); clearTwitterView(); - std::cerr << "WireDialog::~WireDialog()" << std::endl; - delete(mWireQueue); rsEvents->unregisterEventsHandler(mEventHandlerId); } +UserNotify *WireDialog::createUserNotify(QObject *parent) +{ + return new WireUserNotify(rsWire, this, parent); +} + +//QString WireDialog::getHelpString() const +//{ +// int H = misc::getFontSizeFactor("HelpButton").height(); + +// QString hlp_str = tr( +// "

      Boards

    " +// "

    The wire service is a day to day social network service.

    " +// "

    The Wire service allows you to share your pulses, republish someone else's pulses, like " +// "a pulse.

    " +// "

    A pulse is like a channel or board post. It can be a image or a text or both. You can " +// "only see the pulses of the people you follow.

    " +// "

    Wire posts are kept for %2 days, and sync-ed over the last %3 days, unless you change this.

    " +// ).arg( QString::number(2*H) +// , QString::number(rsWire->getDefaultStoragePeriod()/86400) +// , QString::number(rsWire->getDefaultSyncPeriod()/86400)); + +// return hlp_str ; +//} + +//QString WireDialog::text(TextType type) +//{ +// switch (type) { +// case TEXT_NAME: +// return tr("Wire"); +// case TEXT_NEW: +// return tr("Create Wire"); + +//// Dont know what this does +//// case TEXT_TODO: +//// return "Open points:
      " +//// "
    • Subreddits/tag to posts support" +//// "
    • Picture Support" +//// "
    • Navigate channel link" +//// "
    "; + +// case TEXT_YOUR_GROUP: +// return tr("My Wire"); +// case TEXT_SUBSCRIBED_GROUP: +// return tr("Followed Wires"); +// case TEXT_POPULAR_GROUP: +// return tr("Popular Wires"); +// case TEXT_OTHER_GROUP: +// return tr("Other Wires"); +// } + +// return ""; +//} + +//QString WireDialog::icon(IconType type) +//{ +// switch (type) { +// case ICON_NAME: +// return ":/icons/png/postedlinks.png"; +// case ICON_NEW: +// return ":/icons/png/add.png"; +// case ICON_YOUR_GROUP: +// return ""; +// case ICON_SUBSCRIBED_GROUP: +// return ""; +// case ICON_POPULAR_GROUP: +// return ""; +// case ICON_OTHER_GROUP: +// return ""; +// case ICON_SEARCH: +// return ":/images/find.png"; +// case ICON_DEFAULT: +// return ":/icons/png/posted.png"; +// } + +// return ""; +//} + +//bool WireDialog::getGroupData(std::list& groupInfo) +//{ +// std::vector groups; + +// // request all group infos at once + +// if(! rsWire->getGroups(std::list(),groups)) +// return false; + +// /* Save groups to fill icons and description */ + +// for (auto& group: groups) +// groupInfo.push_back(new RsWireGroup(group)); + +// return true; +//} + +bool WireDialog::getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) +{ + // What follows is a hack to replace the GXS group statistics by the actual count of unread messages in wire, + // which should take into account old post versions, discard replies and likes, etc. + + RsWireStatistics s; + bool res = rsWire->getWireStatistics(groupId,s); + + if(!res) + return false; + + stat.mGrpId = groupId; + stat.mNumMsgs = s.mNumberOfPulses; + + stat.mTotalSizeOfMsgs = 0; // hopefuly unused. Required the loading of the full channel data, so not very convenient. + stat.mNumThreadMsgsNew = s.mNumberOfNewPulses; + stat.mNumThreadMsgsUnread = s.mNumberOfUnreadPulses; + stat.mNumChildMsgsNew = 0; + stat.mNumChildMsgsUnread = 0; + + return true; +} + +//GxsGroupDialog *WireDialog::createNewGroupDialog() +//{ +// return new WireGroupDialog(this); +//} + +//GxsGroupDialog *WireDialog::createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) +//{ +// return new WireGroupDialog(mode, groupId, this); +//} + +//int WireDialog::shareKeyType() +//{ +// return GroupShareKey::NO_KEY_SHARE; +//} + +//GxsMessageFrameWidget *WireDialog::createMessageFrameWidget(const RsGxsGroupId &groupId) +//{ +// // to do +//// return new WireListWidgetWithModel(groupId); +//} + void WireDialog::processSettings(bool load) { Settings->beginGroup("WireDialog"); @@ -177,6 +367,33 @@ void WireDialog::refreshGroups() requestGroupData(); } +void WireDialog::showHomeFeed() +{ + mGroupSet = GROUP_SET_SUBSCRIBED; + ui.comboBox_groupSet->setCurrentIndex(GROUP_SET_SUBSCRIBED); + mGroupSelected = nullptr; + showGroups(); +} + +void WireDialog::filterUsers(const QString &text) +{ + QLayout *alayout = ui.groupsWidget->layout(); + QString filterText = text.toLower(); + + for (int i = 0; i < alayout->count(); ++i) + { + QLayoutItem *item = alayout->itemAt(i); + QWidget *widget = item->widget(); + WireGroupItem *groupItem = dynamic_cast(widget); + + if (groupItem) + { + bool visible = filterText.isEmpty() || groupItem->matchesFilter(filterText); + groupItem->setVisible(visible); + } + } +} + void WireDialog::addGroup(QWidget *item) { QLayout *alayout = ui.groupsWidget->layout(); @@ -197,7 +414,7 @@ bool WireDialog::setupPulseAddDialog() mAddDialog->cleanup(); int idx = ui.groupChooser->currentIndex(); - if (idx < 0) { + if (idx < 0 || idx >= (int)mOwnGroups.size()) { std::cerr << "WireDialog::setupPulseAddDialog() ERROR GETTING AuthorId!"; std::cerr << std::endl; @@ -205,9 +422,8 @@ bool WireDialog::setupPulseAddDialog() return false; } - // publishing group. RsWireGroup group = mOwnGroups[idx]; - mAddDialog->setGroup(group.mMeta.mGroupId); + mAddDialog->setGroup(group); return true; } @@ -217,14 +433,12 @@ void WireDialog::subscribe(RsGxsGroupId &groupId) { uint32_t token; rsWire->subscribeToGroup(token, groupId, true); - mWireQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE); } void WireDialog::unsubscribe(RsGxsGroupId &groupId) { uint32_t token; rsWire->subscribeToGroup(token, groupId, false); - mWireQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE); } void WireDialog::notifyGroupSelection(WireGroupItem *item) @@ -388,6 +602,12 @@ void WireDialog::selectGroupSet(int index) if (mGroupSet < 0) { mGroupSet = GROUP_SET_ALL; } + + // show follow label and account filter only when All is selected + bool showFollow = (mGroupSet == GROUP_SET_ALL); + ui.followLabel->setVisible(showFollow); + ui.toolButton_accountFilter->setVisible(showFollow); + showGroups(); } @@ -399,9 +619,26 @@ void WireDialog::selectFilterTime(int index) showSelectedGroups(); } +void WireDialog::toggleSortAscending() +{ + mSortAscending = !mSortAscending; + showSelectedGroups(); +} + +void WireDialog::sortAccountsChanged(QAction *action) +{ + mAccountSortType = action->data().toInt(); + showGroups(); +} + +void WireDialog::hideInactiveChanged(bool checked) +{ + mHideInactiveAccounts = checked; + showGroups(); +} + void WireDialog::showSelectedGroups() { - ui.comboBox_filterTime->setEnabled(false); if (mGroupSelected) { // request data. @@ -419,12 +656,15 @@ void WireDialog::showSelectedGroups() void WireDialog::showGroups() { - ui.comboBox_filterTime->setEnabled(false); deleteGroups(); std::list allGroupIds; + std::vector groupsToShow; + + // Collect groups that match the current filter + rstime_t now = time(nullptr); + rstime_t thirtyDaysAgo = now - (30 * 24 * 60 * 60); - /* depends on the comboBox */ for (auto &it : mAllGroups) { bool add = (mGroupSet == GROUP_SET_ALL); @@ -444,13 +684,39 @@ void WireDialog::showGroups() } } - if (add) { - addGroup(it.second); - // request data. - std::list grpIds; - grpIds.push_back(it.second.mMeta.mGroupId); - allGroupIds.push_back(it.second.mMeta.mGroupId); + // Filter inactive accounts if checkbox is checked + if (add && mHideInactiveAccounts) { + rstime_t lastPost = it.second.mMeta.mLastPost; + if (lastPost == 0 || lastPost < thirtyDaysAgo) { + add = false; + } } + + if (add) { + groupsToShow.push_back(it.second); + } + } + + // Sort groups based on selection + if (mAccountSortType == 0) { + // Sort by name (alphabetical) + std::sort(groupsToShow.begin(), groupsToShow.end(), + [](const RsWireGroup &a, const RsWireGroup &b) { + return QString::fromStdString(a.mMeta.mGroupName).toLower() < + QString::fromStdString(b.mMeta.mGroupName).toLower(); + }); + } else { + // Sort by activity (most recent first) + std::sort(groupsToShow.begin(), groupsToShow.end(), + [](const RsWireGroup &a, const RsWireGroup &b) { + return a.mMeta.mLastPost > b.mMeta.mLastPost; + }); + } + + // Add sorted groups to UI + for (auto &group : groupsToShow) { + addGroup(group); + allGroupIds.push_back(group.mMeta.mGroupId); } requestGroupsPulses(allGroupIds); @@ -464,24 +730,21 @@ void WireDialog::requestGroupData() std::cerr << "WireDialog::requestGroupData()"; std::cerr << std::endl; - RsTokReqOptions opts; - uint32_t token; - opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - mWireQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, 0); -} + std::vector groups; + rsWire->getGroups({}, groups); -bool WireDialog::loadGroupData(const uint32_t &token) -{ - std::cerr << "WireDialog::loadGroupData()"; - std::cerr << std::endl; + updateGroups(groups); + showGroups(); - std::vector groups; - rsWire->getGroupData(token, groups); - - // save list of groups. - updateGroups(groups); - showGroups(); - return true; + if (!mNavigatePendingGroupId.isNull()) { + if (!mNavigatePendingMsgId.isNull()) { + requestPulseFocus(mNavigatePendingGroupId, mNavigatePendingMsgId); + } else { + requestGroupFocus(mNavigatePendingGroupId); + } + mNavigatePendingGroupId.clear(); + mNavigatePendingMsgId.clear(); + } } rstime_t WireDialog::getFilterTimestamp() @@ -507,60 +770,6 @@ rstime_t WireDialog::getFilterTimestamp() return filterTimestamp; } -void WireDialog::acknowledgeGroup(const uint32_t &token, const uint32_t &userType) -{ - /* reload groups */ - std::cerr << "WireDialog::acknowledgeGroup(usertype: " << userType << ")"; - std::cerr << std::endl; - - RsGxsGroupId grpId; - rsWire->acknowledgeGrp(token, grpId); - - refreshGroups(); -} - - -/**************************** Request / Response Filling of Data ************************/ - -void WireDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) -{ - std::cerr << "WireDialog::loadRequest()"; - std::cerr << std::endl; - - if (queue == mWireQueue) - { - /* now switch on req */ - switch(req.mType) - { - case TOKENREQ_GROUPINFO: - switch(req.mAnsType) - { - case RS_TOKREQ_ANSTYPE_DATA: - loadGroupData(req.mToken); - break; - case RS_TOKREQ_ANSTYPE_ACK: - acknowledgeGroup(req.mToken, req.mUserType); - break; - default: - std::cerr << "WireDialog::loadRequest() ERROR: GROUP: INVALID ANS TYPE"; - std::cerr << std::endl; - break; - } - break; - default: - std::cerr << "WireDialog::loadRequest() ERROR: INVALID TYPE"; - std::cerr << std::endl; - break; - } - } -} - - -/**************************** Request / Response Filling of Data ************************/ - - - - /****************************************************************************************/ // TWITTER VIEW. @@ -598,39 +807,36 @@ void WireDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) void WireDialog::PVHreply(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) { std::cerr << "WireDialog::PVHreply() GroupId: " << groupId; - std::cerr << "MsgId: " << msgId; + std::cerr << " MsgId: " << msgId; std::cerr << std::endl; if (setupPulseAddDialog()) { mAddDialog->setReplyTo(groupId, msgId, WIRE_PULSE_TYPE_REPLY); - mAddDialog->show(); } } void WireDialog::PVHrepublish(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) { std::cerr << "WireDialog::PVHrepublish() GroupId: " << groupId; - std::cerr << "MsgId: " << msgId; + std::cerr << " MsgId: " << msgId; std::cerr << std::endl; if (setupPulseAddDialog()) { mAddDialog->setReplyTo(groupId, msgId, WIRE_PULSE_TYPE_REPUBLISH); - mAddDialog->show(); } } void WireDialog::PVHlike(const RsGxsGroupId &groupId, const RsGxsMessageId &msgId) { std::cerr << "WireDialog::PVHlike() GroupId: " << groupId; - std::cerr << "MsgId: " << msgId; + std::cerr << " MsgId: " << msgId; std::cerr << std::endl; if (setupPulseAddDialog()) { mAddDialog->setReplyTo(groupId, msgId, WIRE_PULSE_TYPE_LIKE); - mAddDialog->show(); } } @@ -675,7 +881,6 @@ void WireDialog::PVHfollow(const RsGxsGroupId &groupId) uint32_t token; rsWire->subscribeToGroup(token, groupId, true); - mWireQueue->queueRequest(token, TOKENREQ_GROUPINFO, RS_TOKREQ_ANSTYPE_ACK, WIRE_TOKEN_TYPE_SUBSCRIBE_CHANGE); } void WireDialog::PVHrate(const RsGxsId &authorId) @@ -689,7 +894,6 @@ void WireDialog::PVHrate(const RsGxsId &authorId) void WireDialog::postTestTwitterView() { clearTwitterView(); - std::cerr << "WireDialog::postTestTwitterView()" << std::endl; addTwitterView(new PulseTopLevel(NULL,RsWirePulseSPtr())); addTwitterView(new PulseReply(NULL,RsWirePulseSPtr())); @@ -846,7 +1050,6 @@ void WireDialog::requestPulseFocus(const RsGxsGroupId groupId, const RsGxsMessag void WireDialog::showPulseFocus(const RsGxsGroupId groupId, const RsGxsMessageId msgId) { clearTwitterView(); - std::cerr << "WireDialog::showPulseFocus()" << std::endl; // background thread for loading. RsThread::async([this, groupId, msgId]() @@ -876,8 +1079,6 @@ void WireDialog::showPulseFocus(const RsGxsGroupId groupId, const RsGxsMessageId void WireDialog::postPulseFocus(RsWirePulseSPtr pPulse) { clearTwitterView(); - std::cerr << "WireDialog::postPulseFocus()" << std::endl; - if (!pPulse) { std::cerr << "WireDialog::postPulseFocus() Invalid pulse"; @@ -950,7 +1151,7 @@ void WireDialog::requestGroupFocus(const RsGxsGroupId groupId) void WireDialog::showGroupFocus(const RsGxsGroupId groupId) { clearTwitterView(); - std::cerr << "WireDialog::showGroupFocus()" << std::endl; + // background thread for loading. RsThread::async([this, groupId]() { @@ -980,6 +1181,8 @@ void WireDialog::showGroupFocus(const RsGxsGroupId groupId) void WireDialog::postGroupFocus(RsWireGroupSPtr group, std::list pulses) { + clearTwitterView(); + std::cerr << "WireDialog::postGroupFocus()"; std::cerr << std::endl; @@ -1027,7 +1230,6 @@ void WireDialog::requestGroupsPulses(const std::list& groupIds) void WireDialog::showGroupsPulses(const std::list& groupIds) { clearTwitterView(); - std::cerr << "WireDialog::showGroupPulses()" << std::endl; // background thread for loading. RsThread::async([this, groupIds]() @@ -1054,6 +1256,7 @@ void WireDialog::showGroupsPulses(const std::list& groupIds) void WireDialog::postGroupsPulses(std::list pulses) { + clearTwitterView(); std::cerr << "WireDialog::postGroupsPulses()"; std::cerr << std::endl; @@ -1077,3 +1280,166 @@ void WireDialog::postGroupsPulses(std::list pulses) } } + +void WireDialog::getServiceStatistics(GxsServiceStatistic& stats) const +{ + if(!mCachedGroupStats.empty()) + { + stats = GxsServiceStatistic(); // clears everything + + for(auto& it: mCachedGroupStats) + { + const GxsGroupStatistic& s(it.second); + + stats.mNumMsgs += s.mNumMsgs; + stats.mNumGrps += 1; + stats.mSizeOfMsgs += s.mTotalSizeOfMsgs; + stats.mNumThreadMsgsNew += s.mNumThreadMsgsNew; + stats.mNumThreadMsgsUnread += s.mNumThreadMsgsUnread; + stats.mNumChildMsgsNew += s.mNumChildMsgsNew ; + stats.mNumChildMsgsUnread += s.mNumChildMsgsUnread ; + } + + // Also save the service statistics in conf file, so that we can display it right away at start. + + Settings->beginGroup(mSettingsName); + Settings->setValue("NumMsgs", stats.mNumMsgs ); + Settings->setValue("NumGrps", stats.mNumGrps ); + Settings->setValue("SizeOfMessages", stats.mSizeOfMsgs ); + Settings->setValue("NumThreadMsgsNew", stats.mNumThreadMsgsNew ); + Settings->setValue("NumThreadMsgsUnread",stats.mNumThreadMsgsUnread); + Settings->setValue("NumChildMsgsNew", stats.mNumChildMsgsNew ); + Settings->setValue("NumChildMsgsUnread", stats.mNumChildMsgsUnread ); + Settings->endGroup(); + } + else // Get statistics from settings if no cache is already present: allows to display at start. + { + Settings->beginGroup(mSettingsName); + + stats.mNumMsgs = Settings->value("NumMsgs",QVariant(0)).toInt(); + stats.mNumGrps = Settings->value("NumGrps",QVariant(0)).toInt(); + stats.mSizeOfMsgs = Settings->value("SizeOfMessages",QVariant(0)).toInt(); + stats.mNumThreadMsgsNew = Settings->value("NumThreadMsgsNew",QVariant(0)).toInt(); + stats.mNumThreadMsgsUnread = Settings->value("NumThreadMsgsUnread",QVariant(0)).toInt(); + stats.mNumChildMsgsNew = Settings->value("NumChildMsgsNew",QVariant(0)).toInt(); + stats.mNumChildMsgsUnread = Settings->value("NumChildMsgsUnread",QVariant(0)).toInt(); + + Settings->endGroup(); + } +} + +void WireDialog::updateGroupStatistics(const RsGxsGroupId &groupId) +{ + mGroupStatisticsToUpdate.insert(groupId); + mShouldUpdateGroupStatistics = true; +} + +void WireDialog::updateGroupStatisticsReal(const RsGxsGroupId &groupId) +{ + RsThread::async([this,groupId]() + { + GxsGroupStatistic stats; + + if(! getGroupStatistics(groupId, stats)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to collect group statistics for group " << groupId << std::endl; + return; + } + + RsQThreadUtils::postToObject( [this,stats, groupId]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + +// QTreeWidgetItem *item = ui.scrollAreaWidgetContents->getItemFromId(QString::fromStdString(stats.mGrpId.toStdString())); + +// if (item) +// ui.scrollAreaWidgetContents->setUnreadCount(item, mCountChildMsgs ? (stats.mNumThreadMsgsUnread + stats.mNumChildMsgsUnread) : stats.mNumThreadMsgsUnread); + + mCachedGroupStats[groupId] = stats; + + getUserNotify()->updateIcon(); + + }, this ); + }); +} + +bool WireDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessageId& msgId) +{ + if (groupId.isNull()) { + return false; + } + + // if groups not loaded yet, save for later + if (mAllGroups.empty()) { + mNavigatePendingGroupId = groupId; + mNavigatePendingMsgId = msgId; + return true; + } + + if (!msgId.isNull()) { + requestPulseFocus(groupId, msgId); + } else { + requestGroupFocus(groupId); + } + + return true; +} + +GxsMessageFrameWidget *WireDialog::messageWidget(const RsGxsGroupId &groupId) +{ +// int tabCount = ui.tabWidget->count(); + +// for (int index = 0; index < tabCount; ++index) +// { +// GxsMessageFrameWidget *childWidget = dynamic_cast(ui.tabWidget->widget(index)); + +// if (childWidget && childWidget->groupId() == groupId) +// return childWidget; +// } + + return NULL; +} + +//void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId) +//{ +// if (mInFill) { +// return; +// } + +// if (groupId.isEmpty()) +// { +// auto w = currentWidget(); + +// if(w) +// w->setGroupId(RsGxsGroupId()); + +// return; +// } + +// mGroupId = RsGxsGroupId(groupId.toStdString()); + +// if (mGroupId.isNull()) +// return; + +// /* search exisiting tab */ +// GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); + +// // check that we have at least one tab + +// if(msgWidget) +// ui.messageTabWidget->setCurrentWidget(msgWidget); +// else +// { +// if(useTabs() || ui.messageTabWidget->count()==0) +// { +// msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString())); +// ui.messageTabWidget->setCurrentWidget(msgWidget); +// } +// else +// currentWidget()->setGroupId(mGroupId); +// } +//} diff --git a/retroshare-gui/src/gui/TheWire/WireDialog.h b/retroshare-gui/src/gui/TheWire/WireDialog.h index fcd33ad37..d3ec5347d 100644 --- a/retroshare-gui/src/gui/TheWire/WireDialog.h +++ b/retroshare-gui/src/gui/TheWire/WireDialog.h @@ -34,9 +34,11 @@ #include "gui/TheWire/PulseViewItem.h" #include "gui/TheWire/PulseTopLevel.h" #include "gui/TheWire/PulseReply.h" +#include "gui/gxs/GxsStatisticsProvider.h" - -#include "util/TokenQueue.h" +class UIStateHelper; +class GxsMessageFrameWidget; +class RsGxsIfaceHelper; #define IMAGE_WIRE ":/icons/wire.png" @@ -68,7 +70,7 @@ public: }; //--------------------------------------------------------- -class WireDialog : public MainPage, public TokenResponse, public WireGroupHolder, public PulseViewHolder +class WireDialog : public MainPage, public GxsStatisticsProvider, public WireGroupHolder, public PulseViewHolder { Q_OBJECT @@ -118,15 +120,35 @@ public: void showGroupsPulses(const std::list& groupIds); void postGroupsPulses(std::list pulses); + void getServiceStatistics(GxsServiceStatistic& stats) const override; + + virtual bool navigate(const RsGxsGroupId &groupId, const RsGxsMessageId& msgId); + +protected: + + bool getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat); + UserNotify *createUserNotify(QObject *parent) override; + virtual void updateGroupStatistics(const RsGxsGroupId &groupId); + virtual void updateGroupStatisticsReal(const RsGxsGroupId &groupId); + private slots: void createGroup(); void createPulse(); + void showHomeFeed(); + void filterUsers(const QString &text); void checkUpdate(); void refreshGroups(); void selectGroupSet(int index); void selectFilterTime(int index); + // Filter menu actions + void toggleSortAscending(); + + // Account filter slots + void sortAccountsChanged(QAction *action); + void hideInactiveChanged(bool checked); + // history navigation. void back(); void forward(); @@ -150,16 +172,13 @@ private: // Loading Data. void requestGroupData(); - bool loadGroupData(const uint32_t &token); - void acknowledgeGroup(const uint32_t &token, const uint32_t &userType); - virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req) override; + GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId); int mGroupSet; PulseAddDialog *mAddDialog; WireGroupItem *mGroupSelected; - TokenQueue *mWireQueue; std::map mAllGroups; std::vector mOwnGroups; @@ -170,6 +189,21 @@ private: int32_t mHistoryIndex; std::vector mHistory; + bool mSortAscending; + int mAccountSortType; + bool mHideInactiveAccounts; + + // Members for GxsStatisticsProvider interface support + QString mSettingsName; + RsGxsIfaceHelper *mInterface; + bool mDistSyncAllowed; + std::map mCachedGroupStats; + bool mShouldUpdateGroupStatistics; + std::set mGroupStatisticsToUpdate; + bool mCountChildMsgs; + RsGxsGroupId mNavigatePendingGroupId; + RsGxsMessageId mNavigatePendingMsgId; + UIStateHelper *mStateHelper; /* UI - from Designer */ Ui::WireDialog ui; diff --git a/retroshare-gui/src/gui/TheWire/WireDialog.ui b/retroshare-gui/src/gui/TheWire/WireDialog.ui index 4cdf4a174..3621c54b5 100644 --- a/retroshare-gui/src/gui/TheWire/WireDialog.ui +++ b/retroshare-gui/src/gui/TheWire/WireDialog.ui @@ -87,6 +87,20 @@
    + + + + Home + + + + :/icons/png/home.png:/icons/png/home.png + + + Qt::ToolButtonTextBesideIcon + + + @@ -178,18 +192,49 @@ - - - - 10 - true - + + + Search... - - Who to Follow + + true + + + + + + + 10 + true + + + + Who to Follow + + + + + + + + + + + :/icons/png/options.png:/icons/png/options.png + + + QToolButton::InstantPopup + + + true + + + + + @@ -224,7 +269,7 @@ - + 3 @@ -363,6 +408,20 @@ + + + + + + + + :/icons/mail/filter24.png:/icons/mail/filter24.png + + + QToolButton::InstantPopup + + + @@ -432,6 +491,12 @@ QComboBox
    gui/common/RSComboBox.h
    + + RSTabWidget + QTabWidget +
    gui/common/RSTabWidget.h
    + 1 +
    diff --git a/retroshare-gui/src/gui/TheWire/WireGroupDialog.cpp b/retroshare-gui/src/gui/TheWire/WireGroupDialog.cpp index e832540de..586ea6e7d 100644 --- a/retroshare-gui/src/gui/TheWire/WireGroupDialog.cpp +++ b/retroshare-gui/src/gui/TheWire/WireGroupDialog.cpp @@ -34,7 +34,7 @@ const uint32_t WireCreateEnabledFlags = ( GXS_GROUP_FLAGS_DISTRIBUTION | // GXS_GROUP_FLAGS_PUBLISHSIGN | // GXS_GROUP_FLAGS_SHAREKEYS | // disabled because the UI doesn't handle it yet. - // GXS_GROUP_FLAGS_PERSONALSIGN | + GXS_GROUP_FLAGS_PERSONALSIGN | // GXS_GROUP_FLAGS_COMMENTS | GXS_GROUP_FLAGS_EXTRA | 0); diff --git a/retroshare-gui/src/gui/TheWire/WireGroupItem.cpp b/retroshare-gui/src/gui/TheWire/WireGroupItem.cpp index 1759103f6..de4ac6061 100644 --- a/retroshare-gui/src/gui/TheWire/WireGroupItem.cpp +++ b/retroshare-gui/src/gui/TheWire/WireGroupItem.cpp @@ -210,6 +210,15 @@ const QPixmap *WireGroupItem::getPixmap() return NULL; } +bool WireGroupItem::matchesFilter(const QString &filterText) const +{ + if (filterText.isEmpty()) + return true; + + QString groupName = QString::fromStdString(mGroup.mMeta.mGroupName).toLower(); + return groupName.contains(filterText); +} + void WireGroupItem::editGroupDetails() { RsGxsGroupId groupId = mGroup.mMeta.mGroupId; diff --git a/retroshare-gui/src/gui/TheWire/WireGroupItem.h b/retroshare-gui/src/gui/TheWire/WireGroupItem.h index a7ae14326..522a1ccc2 100644 --- a/retroshare-gui/src/gui/TheWire/WireGroupItem.h +++ b/retroshare-gui/src/gui/TheWire/WireGroupItem.h @@ -51,6 +51,7 @@ public: const QPixmap *getPixmap(); RsGxsGroupId &groupId(); + bool matchesFilter(const QString &filterText) const; private slots: void show(); diff --git a/retroshare-gui/src/gui/TheWire/WireUserNotify.cpp b/retroshare-gui/src/gui/TheWire/WireUserNotify.cpp new file mode 100644 index 000000000..59db49d00 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireUserNotify.cpp @@ -0,0 +1,62 @@ +/******************************************************************************* + * retroshare-gui/src/gui/TheWire/WireUserNotify.cpp * + * * + * Copyright (C) 2014 by Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "retroshare/rswire.h" +#include "WireUserNotify.h" +#include "gui/MainWindow.h" +#include "gui/common/FilesDefs.h" + +WireUserNotify::WireUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent) : + GxsUserNotify(ifaceImpl, g, parent) +{ +} + +bool WireUserNotify::hasSetting(QString *name, QString *group) +{ + if (name) *name = tr("Wire Post"); + if (group) *group = "Wire"; + + return true; +} + +QIcon WireUserNotify::getIcon() +{ + return FilesDefs::getIconFromQtResourcePath(":/icons/png/wire-circle.png"); +} + +QIcon WireUserNotify::getMainIcon(bool hasNew) +{ + return hasNew ? FilesDefs::getIconFromQtResourcePath(":/icons/png/wire-notify.png") : FilesDefs::getIconFromQtResourcePath(":/icons/png/wire-circle.png"); +} + +//QString WireUserNotify::getTrayMessage(bool plural) +//{ +// return plural ? tr("You have %1 new wire pulse") : tr("You have %1 new wire pulse"); +//} + +//QString WireUserNotify::getNotifyMessage(bool plural) +//{ +// return plural ? tr("%1 new wire pulse") : tr("%1 new wire pulse"); +//} + +void WireUserNotify::iconClicked() +{ + MainWindow::showWindow(MainWindow::Wire); +} diff --git a/retroshare-gui/src/gui/TheWire/WireUserNotify.h b/retroshare-gui/src/gui/TheWire/WireUserNotify.h new file mode 100644 index 000000000..bf160bec2 --- /dev/null +++ b/retroshare-gui/src/gui/TheWire/WireUserNotify.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * retroshare-gui/src/gui/TheWire/WireUserNotify.h * + * * + * Copyright (C) 2014 by Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef WIREUSERNOTIFY_H +#define WIREUSERNOTIFY_H + +#include "gui/gxs/GxsUserNotify.h" + +class WireUserNotify : public GxsUserNotify +{ + Q_OBJECT + +public: + explicit WireUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent = 0); + + virtual bool hasSetting(QString *name, QString *group) override; + +private: + virtual QIcon getIcon() override; + virtual QIcon getMainIcon(bool hasNew) override; + + //virtual QString getTrayMessage(bool plural) override; + //virtual QString getNotifyMessage(bool plural) override; + + virtual void iconClicked() override; +}; + +#endif // WIREUSERNOTIFY_H \ No newline at end of file diff --git a/retroshare-gui/src/gui/feeds/FeedItem.h b/retroshare-gui/src/gui/feeds/FeedItem.h index f11424520..0d3876da6 100644 --- a/retroshare-gui/src/gui/feeds/FeedItem.h +++ b/retroshare-gui/src/gui/feeds/FeedItem.h @@ -38,6 +38,9 @@ enum class RsFeedTypeFlags: uint32_t { RS_FEED_TYPE_POSTED = 0x1000, RS_FEED_TYPE_SECURITY_IP = 0x2000, RS_FEED_TYPE_CIRCLE = 0x4000, +#ifdef RS_USE_WIRE + RS_FEED_TYPE_WIRE = 0x8000, +#endif RS_FEED_ITEM_PEER_CONNECT = RS_FEED_TYPE_PEER | 0x0001, RS_FEED_ITEM_PEER_DISCONNECT = RS_FEED_TYPE_PEER | 0x0002, diff --git a/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.cpp b/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.cpp new file mode 100644 index 000000000..7a0674d09 --- /dev/null +++ b/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.cpp @@ -0,0 +1,281 @@ +/******************************************************************************* + * gui/feeds/WireNotifyGroupItem.cpp * + * * + * Copyright (c) 2014, Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "WireNotifyGroupItem.h" +#include "ui_WireNotifyGroupItem.h" + +#include "FeedHolder.h" +#include "util/qtthreadsutils.h" +#include "gui/common/FilesDefs.h" +#include "gui/RetroShareLink.h" +#include "gui/gxs/GxsIdDetails.h" +#include "gui/MainWindow.h" +#include "gui/TheWire/WireDialog.h" +#include "util/DateTime.h" + +/**** + * #define DEBUG_ITEM 1 + ****/ + +WireNotifyGroupItem::WireNotifyGroupItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, bool isHome, bool autoUpdate, RsWireEventCode eventCode) : + GxsGroupFeedItem(feedHolder, feedId, groupId, isHome, rsWire, autoUpdate), + mEventCode(eventCode) +{ + setup(); + requestGroup(); + addEventHandler(); +} + +void WireNotifyGroupItem::addEventHandler() +{ + mEventHandlerId = 0; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) + { + RsQThreadUtils::postToObject([=]() + { + const auto *e = dynamic_cast(event.get()); + + if(!e || e->mWireGroupId != this->groupId()) + return; + + switch(e->mWireEventCode) + { + case RsWireEventCode::FOLLOW_STATUS_CHANGED: + case RsWireEventCode::WIRE_UPDATED: + mGroup = RsWireGroup(); + requestGroup(); + break; + default: + break; + } + }, this ); + }, mEventHandlerId, RsEventType::WIRE ); +} + +WireNotifyGroupItem::~WireNotifyGroupItem() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); + delete(ui); +} + +void WireNotifyGroupItem::setup() +{ + /* Invoke the Qt Designer generated object setup routine */ + ui = new(Ui::WireNotifyGroupItem); + ui->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose, true); + + /* clear ui */ + ui->nameLabel->setText(tr("Loading...")); + ui->titleLabel->clear(); + ui->descLabel->clear(); + + /* general ones */ + connect(ui->expandButton, SIGNAL(clicked()), this, SLOT(toggle())); + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(removeItem())); + + /* specific */ + connect(ui->subscribeButton, SIGNAL(clicked()), this, SLOT(subscribeWire())); + connect(ui->copyLinkButton, SIGNAL(clicked()), this, SLOT(copyGroupLink())); + connect(ui->nameLabel, SIGNAL(linkActivated(QString)), this, SLOT(openWireGroup())); + + //ui->copyLinkButton->hide(); // No link type at this moment + ui->expandFrame->hide(); +} + +bool WireNotifyGroupItem::setGroup(const RsWireGroup &group) +{ + if (groupId() != group.mMeta.mGroupId) { + std::cerr << "WireNotifyGroupItem::setContent() - Wrong id, cannot set post"; + std::cerr << std::endl; + return false; + } + mGroup = group; + fill(); + + return true; +} + +void WireNotifyGroupItem::loadGroup() +{ + RsThread::async([this]() + { + // 1 - get group data + +#ifdef DEBUG_ITEM + std::cerr << "Retrieving post data for post " << mThreadId << std::endl; +#endif + + std::vector groups; + const std::list groupIds = { groupId() }; + + if(!rsWire->getGroups(groupIds,groups)) + { + RsErr() << "WireNotifyGroupItem::loadGroup() ERROR getting data" << std::endl; + deferred_update(); + return; + } + + if (groups.size() != 1) + { + std::cerr << "WireNotifyGroupItem::loadGroup() Wrong number of Items"; + std::cerr << std::endl; + deferred_update(); + return; + } + RsWireGroup group(groups[0]); + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + setGroup(group); + + }, this ); + }); +} + +QString WireNotifyGroupItem::groupName() +{ + return QString::fromUtf8(mGroup.mMeta.mGroupName.c_str()); +} + +void WireNotifyGroupItem::fill() +{ + /* fill in */ + +#ifdef DEBUG_ITEM + std::cerr << "WireNotifyGroupItem::fill()"; + std::cerr << std::endl; +#endif + + // No link type at this moment + RetroShareLink link = RetroShareLink::createGxsGroupLink(RetroShareLink::TYPE_WIRE, mGroup.mMeta.mGroupId, groupName()); + ui->nameLabel->setText(link.toHtml()); + + ui->descLabel->setText(QString::fromUtf8(mGroup.mTagline.c_str())); + + if (mGroup.mHeadshot.mData != NULL) { + QPixmap wireImage; + GxsIdDetails::loadPixmapFromData(mGroup.mHeadshot.mData, mGroup.mHeadshot.mSize, wireImage,GxsIdDetails::ORIGINAL); + ui->logoLabel->setPixmap(QPixmap(wireImage)); + } else { + ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/wire.png")); + } + + //TODO - nice icon for subscribed group +// if (IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) { +// ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/wire.png")); +// } else { +// ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/wire.png")); +// } + + if (IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { + ui->subscribeButton->setEnabled(false); + } else { + ui->subscribeButton->setEnabled(true); + } + + if(mGroup.mMeta.mLastPost==0) + ui->infoLastPost->setText(tr("Never")); + else + ui->infoLastPost->setText(DateTime::formatLongDateTime(mGroup.mMeta.mLastPost)); + + // Set title based on event type + switch(mEventCode) + { + case RsWireEventCode::FOLLOW_STATUS_CHANGED: + ui->titleLabel->setText(tr("Now Following")); + break; + case RsWireEventCode::WIRE_UPDATED: + ui->titleLabel->setText(tr("Wire Updated")); + break; + case RsWireEventCode::NEW_WIRE: + default: + ui->titleLabel->setText(tr("New Wire")); + break; + } + + if (mIsHome) + { + /* disable buttons */ + ui->clearButton->setEnabled(false); + } +} + +void WireNotifyGroupItem::toggle() +{ + expand(ui->expandFrame->isHidden()); +} + +void WireNotifyGroupItem::doExpand(bool open) +{ + if (mFeedHolder) + { + mFeedHolder->lockLayout(this, true); + } + + if (open) + { + ui->expandFrame->show(); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); + ui->expandButton->setToolTip(tr("Hide")); + } + else + { + ui->expandFrame->hide(); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); + ui->expandButton->setToolTip(tr("Expand")); + } + + emit sizeChanged(this); + + if (mFeedHolder) + { + mFeedHolder->lockLayout(this, false); + } +} + +void WireNotifyGroupItem::subscribeWire() +{ +#ifdef DEBUG_ITEM + std::cerr << "WireNotifyGroupItem::subscribeWire()"; + std::cerr << std::endl; +#endif + + //subscribe(); does not work for wire, bug? -> GxsGroupFeedItem::subscribe() + + uint32_t token; + rsWire->subscribeToGroup(token, mGroup.mMeta.mGroupId, true); +} + +void WireNotifyGroupItem::openWireGroup() +{ + MainWindow::showWindow(MainWindow::Wire); + WireDialog *wireDialog = dynamic_cast(MainWindow::getPage(MainWindow::Wire)); + if (wireDialog) + { + wireDialog->navigate(mGroup.mMeta.mGroupId, RsGxsMessageId()); + } + removeItem(); +} \ No newline at end of file diff --git a/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.h b/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.h new file mode 100644 index 000000000..7110feb4f --- /dev/null +++ b/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.h @@ -0,0 +1,74 @@ +/******************************************************************************* + * gui/feeds/WireNotifyGroupItem.h * + * * + * Copyright (c) 2014, Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef WIRENOTIFYGROUPITEM_H +#define WIRENOTIFYGROUPITEM_H + +#include +#include "gui/feeds/GxsGroupFeedItem.h" + +namespace Ui { +class WireNotifyGroupItem; +} + +class FeedHolder; + +class WireNotifyGroupItem : public GxsGroupFeedItem +{ + Q_OBJECT + +public: + /** Default Constructor */ + WireNotifyGroupItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, bool isHome, bool autoUpdate, RsWireEventCode eventCode = RsWireEventCode::NEW_WIRE); + ~WireNotifyGroupItem(); + + bool setGroup(const RsWireGroup &group); + + uint64_t uniqueIdentifier() const override { return hash_64bits("WireNotifyGroupItem " + groupId().toStdString()) ; } + +protected: + /* FeedItem */ + virtual void doExpand(bool open); + + /* GxsGroupFeedItem */ + virtual QString groupName(); + virtual void loadGroup() override; + virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_WIRE; } + +private slots: + void toggle() override; + void subscribeWire(); + void openWireGroup(); + +private: + void fill(); + void setup(); + void addEventHandler(); + +private: + RsWireGroup mGroup; + RsWireEventCode mEventCode; + + /** Qt Designer generated object */ + Ui::WireNotifyGroupItem *ui; + RsEventsHandlerId_t mEventHandlerId; +}; + +#endif // WIRENOTIFYGROUPITEM_H \ No newline at end of file diff --git a/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.ui b/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.ui new file mode 100644 index 000000000..7c3548c12 --- /dev/null +++ b/retroshare-gui/src/gui/feeds/WireNotifyGroupItem.ui @@ -0,0 +1,403 @@ + + + WireNotifyGroupItem + + + + 0 + 0 + 618 + 191 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 215 + 215 + 215 + + + + + + + 215 + 215 + 215 + + + + + + + + + 0 + 0 + 0 + + + + + + + 215 + 215 + 215 + + + + + + + 215 + 215 + 215 + + + + + + + + + 154 + 154 + 154 + + + + + + + 215 + 215 + 215 + + + + + + + 215 + 215 + 215 + + + + + + + + true + + + QFrame::Box + + + QFrame::Sunken + + + + + + + + + 70 + 70 + + + + + 70 + 70 + + + + + + + :/icons/png/posted.png + + + true + + + + + + + + + + 0 + 0 + + + + + 11 + 75 + true + true + + + + Wire + + + + + + + + 11 + 75 + true + true + + + + name + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + true + + + + Last activity + + + + + + + + 75 + true + true + + + + TextLabel + + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + <html><head/><body><p>Follow to User</p></body></html> + + + Follow + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 254 + 28 + + + + + + + + Qt::NoFocus + + + Copy RetroShare Link + + + + :/icons/png/copy.png:/icons/png/copy.png + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Expand + + + + :/icons/png/down-arrow.png:/icons/png/down-arrow.png + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Remove Item + + + + :/icons/png/exit2.png:/icons/png/exit2.png + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Wire Description + + + + + + + + + :/images/contacts24.png + + + + + + + + 0 + 0 + + + + Description + of Wire + + + + + + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/feeds/WireNotifyPostItem.cpp b/retroshare-gui/src/gui/feeds/WireNotifyPostItem.cpp new file mode 100644 index 000000000..824af3131 --- /dev/null +++ b/retroshare-gui/src/gui/feeds/WireNotifyPostItem.cpp @@ -0,0 +1,522 @@ +/******************************************************************************* + * gui/feeds/WireNotifyPostItem.cpp * + * * + * Copyright (c) 2012, Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include + +#include "WireNotifyPostItem.h" +#include "ui_WireNotifyPostItem.h" + +#include "FeedHolder.h" +#include "util/qtthreadsutils.h" +#include "gui/RetroShareLink.h" +#include "gui/common/FilesDefs.h" +#include "gui/gxs/GxsIdDetails.h" +#include "util/stringutil.h" +#include "util/DateTime.h" +#include "util/misc.h" + +#include +#include + +WireNotifyPostItem::WireNotifyPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId& groupId, const RsGxsMessageId &messageId, bool autoUpdate, const std::set &older_versions) : + GxsFeedItem(feedHolder, feedId, groupId, messageId, false, rsWire, autoUpdate) +{ + mLoadingStatus = LOADING_STATUS_NO_DATA; + mLoadingGroup = false; + mLoadingMessage = false; + mLoadingComment = false; + + QVector v; + //bool self = false; + + for(std::set::const_iterator it(older_versions.begin());it!=older_versions.end();++it) + v.push_back(*it) ; + + if(older_versions.find(messageId) == older_versions.end()) + v.push_back(messageId); + + setMessageVersions(v) ; + setup(); +} + +void WireNotifyPostItem::paintEvent(QPaintEvent *e) +{ + /* This method employs a trick to trigger a deferred loading. The post and group is requested only + * when actually displayed on the screen. */ + + if(mLoadingStatus != LOADING_STATUS_FILLED && !mGroupMeta.mGroupId.isNull() && !mPulse.mMeta.mMsgId.isNull() ) + mLoadingStatus = LOADING_STATUS_HAS_DATA; + + if(mGroupMeta.mGroupId.isNull() && !mLoadingGroup) + requestGroup(); + + if(mPulse.mMeta.mMsgId.isNull() && !mLoadingMessage) + requestMessage(); + + switch(mLoadingStatus) + { + case LOADING_STATUS_FILLED: + case LOADING_STATUS_NO_DATA: + default: + break; + + case LOADING_STATUS_HAS_DATA: + fill(); + setGroup(mGroup); + mLoadingStatus = LOADING_STATUS_FILLED; + break; + } + + GxsFeedItem::paintEvent(e) ; +} + +WireNotifyPostItem::~WireNotifyPostItem() +{ + auto timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(GROUP_ITEM_LOADING_TIMEOUT_ms); + + while( (mLoadingGroup || mLoadingMessage || mLoadingComment) + && std::chrono::steady_clock::now() < timeout) + { + RsDbg() << __PRETTY_FUNCTION__ << " is Waiting for " + << (mLoadingGroup ? "Group " : "") + << (mLoadingMessage ? "Message " : "") + << (mLoadingComment ? "Comment " : "") + << "loading." << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + delete ui; +} + +bool WireNotifyPostItem::isUnread() const +{ + return IS_MSG_UNREAD(mPulse.mMeta.mMsgStatus) ; +} + +void WireNotifyPostItem::setup() +{ + /* Invoke the Qt Designer generated object setup routine */ + + ui = new Ui::WireNotifyPostItem; + ui->setupUi(this); + + // Manually set icons to allow to use clever resource sharing that is missing in Qt for Icons loaded from Qt resource file. + // This is particularly important here because a wire may contain many posts, so duplicating the QImages here is deadly for the + // memory. + +// ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default-video.png")); + //ui->warn_image_label->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/status_unknown.png")); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png")); + //ui->editButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/pencil-edit-button.png")); +// ui->copyLinkButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/copy.png")); +// ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/down-arrow.png")); +// ui->readAndClearButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/correct.png")); +// ui->clearButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/exit2.png")); + + setAttribute(Qt::WA_DeleteOnClose, true); + + mInFill = false; + mCloseOnRead = false; + + /* clear ui */ + ui->titleLabel->setText(tr("Loading...")); + ui->titleLabel->setOpenExternalLinks(false); //To get linkActivated working + connect(ui->titleLabel, SIGNAL(linkActivated(QString)), this, SLOT(on_linkActivated(QString))); + ui->datetimelabel->clear(); +// ui->newCommentLabel->hide(); +// ui->commLabel->hide(); + + /* general ones */ + connect(ui->expandButton, SIGNAL(clicked()), this, SLOT(toggle())); + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(removeItem())); + + /* specific */ + connect(ui->readAndClearButton, SIGNAL(clicked()), this, SLOT(readAndClearItem())); + connect(ui->unsubscribeButton, SIGNAL(clicked()), this, SLOT(unsubscribe())); + + //connect(ui->editButton, SIGNAL(clicked()), this, SLOT(edit(void))); + connect(ui->copyLinkButton, SIGNAL(clicked()), this, SLOT(copyMessageLink())); + + connect(ui->readButton, SIGNAL(toggled(bool)), this, SLOT(readToggled(bool))); + + // hide unsubscribe button not necessary + ui->unsubscribeButton->hide(); + //ui->warn_image_label->hide(); + //ui->warning_label->hide(); + + ui->titleLabel->setMinimumWidth(100); + ui->subjectLabel->setMinimumWidth(100); + //ui->warning_label->setMinimumWidth(100); + + ui->feedFrame->setProperty("new", false); + ui->feedFrame->style()->unpolish(ui->feedFrame); + ui->feedFrame->style()->polish( ui->feedFrame); + + ui->expandFrame->hide(); +} + +bool WireNotifyPostItem::setPost(const RsWirePulse &pulse, bool doFill) +{ + if (groupId() != pulse.mMeta.mGroupId || messageId() != pulse.mMeta.mMsgId) { + std::cerr << "WireNotifyPostItem::setPost() - Wrong id, cannot set post"; + std::cerr << std::endl; + return false; + } + + mPulse = pulse; + + if (doFill) { + fill(); + } + +// updateItem(); + + return true; +} + +void WireNotifyPostItem::expandFill(bool first) +{ + GxsFeedItem::expandFill(first); + + if (first) { +// fillExpandFrame(); + } +} + +QString WireNotifyPostItem::messageName() +{ + if (!mPulse.mMeta.mMsgName.empty()) + return QString::fromUtf8(mPulse.mMeta.mMsgName.c_str()); + + return tr("New Pulse"); +} + +void WireNotifyPostItem::loadMessage() +{ +#ifdef DEBUG_ITEM + std::cerr << "WireNotifyPostItem::loadMessage()"; + std::cerr << std::endl; +#endif + mLoadingMessage = true; + + RsGxsGroupId grpId = groupId(); + RsGxsMessageId msgId = messageId(); + + RsThread::async([this, grpId, msgId]() + { + RsWirePulseSPtr pPulse; + + if (!rsWire->getWirePulse(grpId, msgId, pPulse)) + { + RsErr() << "WireNotifyPostItem::loadMessage() ERROR getting pulse" << std::endl; + mLoadingMessage = false; + deferred_update(); + return; + } + + if (pPulse) + { +#ifdef DEBUG_ITEM + std::cerr << (void*)this << ": Obtained post, with msgId = " << pPulse->mMeta.mMsgId << std::endl; +#endif + const RsWirePulse pulse(*pPulse); + + RsQThreadUtils::postToObject([pulse, this]() + { + mPulse = pulse; + mLoadingMessage = false; + update(); // triggers paintEvent if needed + }, this); + } + else + { + RsQThreadUtils::postToObject([this]() + { + removeItem(); + mLoadingMessage = false; + update(); + }, this); + } + }); +} + +void WireNotifyPostItem::loadComment() +{ + // TODO: implement Wire comment loading when Wire comments are supported +} + +void WireNotifyPostItem::loadGroup() +{ +#ifdef DEBUG_ITEM + std::cerr << "WireNotifyGroupItem::loadGroup()"; + std::cerr << std::endl; +#endif + mLoadingGroup = true; + + RsThread::async([this]() + { + // 1 - get group data + +// std::vector groups; +// std::list> groups; + const std::list groupIds = { groupId() }; + std::vector groups; + if(!rsWire->getGroups(groupIds,groups)) // would be better to call channel Summaries for a single group + { + RsErr() << "WireNotifyPostItem::loadGroup() ERROR getting data" << std::endl; + mLoadingGroup = false; + deferred_update(); + return; + } + + if (groups.size() != 1) + { + std::cerr << "WireNotifyPostItem::loadGroup() Wrong number of Items"; + std::cerr << std::endl; + mLoadingGroup = false; + deferred_update(); + return; + } + RsWireGroup group = groups[0]; + + RsQThreadUtils::postToObject( [group,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete */ + + mGroup = group; + mGroupMeta = group.mMeta; + mLoadingGroup = false; + + update(); // triggers paintEvent if needed + + }, this ); + }); +} + +QString WireNotifyPostItem::groupName() +{ + return QString::fromUtf8(mGroupMeta.mGroupName.c_str()); +} + +void WireNotifyPostItem::doExpand(bool open) +{ + if (mFeedHolder) + { + mFeedHolder->lockLayout(this, true); + } + + if (open) + { + ui->expandFrame->show(); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/up-arrow.png"))); + ui->expandButton->setToolTip(tr("Hide")); + +// readToggled(false); + } + else + { + ui->expandFrame->hide(); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/icons/png/down-arrow.png"))); + ui->expandButton->setToolTip(tr("Expand")); + } + + emit sizeChanged(this); + + if (mFeedHolder) + { + mFeedHolder->lockLayout(this, false); + } +} + +void WireNotifyPostItem::fill() +{ + /* fill in */ + +// if (isLoading()) { + // /* Wait for all requests */ + //return; +// } + +#ifdef DEBUG_ITEM + std::cerr << "WireNotifyPostItem::fill()"; + std::cerr << std::endl; +#endif + + mInFill = true; + + QString title; + QString msgText; + + // Determine pulse type for display + QString pulseTypeStr; + if (mPulse.mPulseType & WIRE_PULSE_TYPE_LIKE) { + pulseTypeStr = tr("Like"); + } else if (mPulse.mPulseType & WIRE_PULSE_TYPE_REPUBLISH) { + pulseTypeStr = tr("Republish"); + } else if (mPulse.mPulseType & WIRE_PULSE_TYPE_REPLY) { + pulseTypeStr = tr("Reply"); + } else { + pulseTypeStr = tr("Pulse"); + } + + if (mCloseOnRead && !IS_MSG_NEW(mPulse.mMeta.mMsgStatus)) { + removeItem(); + } + + msgText = pulseTypeStr + ": "; + RetroShareLink msgLink = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_WIRE, mPulse.mMeta.mGroupId, mPulse.mMeta.mMsgId, messageName()); + msgText += msgLink.toHtml(); + ui->subjectLabel->setText(msgText); + + ui->pulseMessage->setText(QString::fromUtf8(mPulse.mPulseText.c_str())); + ui->datetimelabel->setText(DateTime::formatLongDateTime(mPulse.mRefPublishTs)); + + if (IS_GROUP_SUBSCRIBED(mGroupMeta.mSubscribeFlags) || IS_GROUP_ADMIN(mGroupMeta.mSubscribeFlags)) + { + ui->unsubscribeButton->setEnabled(true); + } + else + { + ui->unsubscribeButton->setEnabled(false); + } + ui->readButton->hide(); + ui->newLabel->hide(); + ui->copyLinkButton->hide(); + + if (IS_MSG_NEW(mPulse.mMeta.mMsgStatus)) { + mCloseOnRead = true; + } + + mInFill = false; +} + +void WireNotifyPostItem::setReadStatus(bool isNew, bool isUnread) +{ + if (isNew) + mPulse.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_NEW; + else + mPulse.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_NEW; + + if (isUnread) + { + mPulse.mMeta.mMsgStatus |= GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD; + whileBlocking(ui->readButton)->setChecked(true); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png")); + } + else + { + mPulse.mMeta.mMsgStatus &= ~GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD; + whileBlocking(ui->readButton)->setChecked(false); + ui->readButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png")); + } + + //ui->newLabel->setVisible(isNew); + + //ui->feedFrame->setProperty("new", isNew); + //ui->feedFrame->style()->unpolish(ui->feedFrame); + //ui->feedFrame->style()->polish( ui->feedFrame); +} + +void WireNotifyPostItem::setGroup(const RsWireGroup &group) +{ + ui->groupName->setText(QString::fromStdString(group.mMeta.mGroupName)); + ui->groupName->setToolTip(QString::fromStdString(group.mMeta.mGroupName) + "@" + QString::fromStdString(group.mMeta.mAuthorId.toStdString())); + ui->groupName->hide(); + + QString title; + + // Determine pulse type for title + QString pulseTypeStr; + if (mPulse.mPulseType & WIRE_PULSE_TYPE_LIKE) { + pulseTypeStr = tr("New Like"); + } else if (mPulse.mPulseType & WIRE_PULSE_TYPE_REPUBLISH) { + pulseTypeStr = tr("New Republish"); + } else if (mPulse.mPulseType & WIRE_PULSE_TYPE_REPLY) { + pulseTypeStr = tr("New Reply"); + } else { + pulseTypeStr = tr("Wire Feed"); + } + + title = pulseTypeStr + ": "; + RetroShareLink link = RetroShareLink::createGxsGroupLink(RetroShareLink::TYPE_WIRE, group.mMeta.mGroupId, groupName()); + title += link.toHtml(); + ui->titleLabel->setText(title); + + if (group.mHeadshot.mData ) + { + QPixmap pixmap; + if (GxsIdDetails::loadPixmapFromData( + group.mHeadshot.mData, + group.mHeadshot.mSize, + pixmap,GxsIdDetails::ORIGINAL)) + { + ui->logoLabel->setPixmap(pixmap); + } + } + else + { + // default. + QPixmap pixmap = FilesDefs::getPixmapFromQtResourcePath(":/icons/wire.png"); + ui->logoLabel->setPixmap(pixmap); + } + +} + +/*********** SPECIFIC FUNCTIONS ***********************/ + +void WireNotifyPostItem::readAndClearItem() +{ +#ifdef DEBUG_ITEM + std::cerr << "WireNotifyPostItem::readAndClearItem()"; + std::cerr << std::endl; +#endif + readToggled(false); + removeItem(); +} + +void WireNotifyPostItem::readToggled(bool /*checked*/) +{ + if (mInFill) { + return; + } + + mCloseOnRead = false; + + RsGxsGrpMsgIdPair msgPair = std::make_pair(groupId(), messageId()); + + //rsWire->setMessageReadStatus(msgPair, isUnread()); + + //setReadStatus(false, checked); // Updated by events +} + +void WireNotifyPostItem::on_linkActivated(QString link) +{ + RetroShareLink rsLink(link); + + if (rsLink.valid() ) { + QList rsLinks; + rsLinks.append(rsLink); + RetroShareLink::process(rsLinks); + removeItem(); + return; + } +} diff --git a/retroshare-gui/src/gui/feeds/WireNotifyPostItem.h b/retroshare-gui/src/gui/feeds/WireNotifyPostItem.h new file mode 100644 index 000000000..1dbb4c5ad --- /dev/null +++ b/retroshare-gui/src/gui/feeds/WireNotifyPostItem.h @@ -0,0 +1,104 @@ +/******************************************************************************* + * gui/feeds/WireNotifyPostItem.h * + * * + * Copyright (c) 2012, Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#ifndef WIRENOTIFYPOSTITEM_H +#define WIRENOTIFYPOSTITEM_H + +#include +#include +#include +#include "gui/feeds/GxsFeedItem.h" +#include "gui/feeds/GxsGroupFeedItem.h" + +namespace Ui { +class WireNotifyPostItem; +} + +class FeedHolder; +class SubFileItem; + +class WireNotifyPostItem : public GxsFeedItem +{ + Q_OBJECT + +public: + WireNotifyPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId& groupId, const RsGxsMessageId &messageId, bool autoUpdate, const std::set& older_versions = std::set()); + + virtual ~WireNotifyPostItem(); + + uint64_t uniqueIdentifier() const override { return hash_64bits("WireNotifyPostItem " + messageId().toStdString()); } + + bool setPost(const RsWirePulse& pulse, bool doFill = true); + void setGroup(const RsWireGroup &group); + + bool isUnread() const ; + void setReadStatus(bool isNew, bool isUnread); + +protected: + + /* FeedItem */ + virtual void doExpand(bool open); + virtual void expandFill(bool first); + + virtual void paintEvent(QPaintEvent *) override; + + /* GxsGroupFeedItem */ + virtual QString groupName(); + virtual void loadGroup() override; + virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_WIRE; } + + /* GxsFeedItem */ +// virtual QString messageName(); + virtual void loadMessage(); + virtual void loadComment(); + virtual QString messageName() override; + +private slots: + /* default stuff */ + //void toggle() override; + void readAndClearItem(); + void readToggled(bool checked); + + void on_linkActivated(QString link); +private: + void setup(); + void fill(); + +private: + bool mInFill; + bool mCloseOnRead; + + LoadingStatus mLoadingStatus; + + bool mLoadingMessage; + bool mLoadingGroup; + bool mLoadingComment; + + + RsGroupMetaData mGroupMeta; + RsWirePulse mPulse; + RsWireGroup mGroup; + + /** Qt Designer generated object */ + Ui::WireNotifyPostItem *ui; +}; + +Q_DECLARE_METATYPE(RsWirePulse) + +#endif // WIRENOTIFYPOSTITEM_H diff --git a/retroshare-gui/src/gui/feeds/WireNotifyPostItem.ui b/retroshare-gui/src/gui/feeds/WireNotifyPostItem.ui new file mode 100644 index 000000000..fbbf716cb --- /dev/null +++ b/retroshare-gui/src/gui/feeds/WireNotifyPostItem.ui @@ -0,0 +1,418 @@ + + + WireNotifyPostItem + + + + 0 + 0 + 822 + 175 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + 6 + + + 1 + + + + + + 0 + 0 + + + + true + + + QFrame::Shape::Box + + + QFrame::Shadow::Sunken + + + + + + + + + + + 70 + 70 + + + + + 70 + 70 + + + + TextLabel + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 20 + + + + + + + + + + + + + + + 12 + true + true + + + + TextLabel + + + + + + + + 0 + 0 + + + + + 11 + true + true + + + + Wire Subject + + + false + + + true + + + + + + + + true + + + + DateTime + + + + + + + New + + + + + + + + + + 11 + + + + TextLabel + + + + + + + 8 + + + + + + 11 + false + true + + + + TextLabel + + + true + + + + + + + + 24 + 16777215 + + + + Qt::FocusPolicy::NoFocus + + + Toggle Message Read Status + + + true + + + false + + + false + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + Unsubscribe From Wire + + + Unsubscribe + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + Copy RetroShare Link + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + Expand + + + + :/icons/png/down-arrow.png:/icons/png/down-arrow.png + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + Set as read and remove item + + + + :/icons/png/correct.png:/icons/png/correct.png + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + Remove Item + + + + :/icons/png/exit2.png:/icons/png/exit2.png + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Shape::Box + + + QFrame::Shadow::Sunken + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + true + + + 6 + + + -1 + + + true + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByKeyboard|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 0 + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index 69fb0dcb7..ef2089b4c 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -72,7 +72,7 @@ static const uint32_t DELAY_BETWEEN_GROUP_STATISTICS_UPDATE = 120; // do not upd /** Constructor */ GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl,const QString& settings_name, QWidget *parent,bool allow_dist_sync) -: MainPage(parent),mSettingsName(settings_name) +: MainPage(parent), mSettingsName(settings_name) { /* Invoke the Qt Designer generated object setup routine */ ui = new Ui::GxsGroupFrameDialog(); @@ -173,7 +173,7 @@ void GxsGroupFrameDialog::initUi() processSettings(true); if (groupFrameSettingsType() != GroupFrameSettings::Nothing) { - connect(RsGUIEventManager::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); + connect(RsGUIEventManager::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); settingsChanged(); } @@ -470,9 +470,7 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) uint32_t current_store_time = checkDelay(mInterface->getStoragePeriod(mGroupId))/86400 ; uint32_t current_sync_time = checkDelay(mInterface->getSyncPeriod(mGroupId))/86400 ; -#ifdef DEBUG_GROUPFRAMEDIALOG std::cerr << "Got sync=" << current_sync_time << ". store=" << current_store_time << std::endl; -#endif QAction *actnn = NULL; QMenu *ctxMenu2 = contextMnu.addMenu(tr("Synchronise posts of last...")) ; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h index 81bba553d..c2d75a23c 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h @@ -27,6 +27,7 @@ #include "gui/RetroShareLink.h" #include "gui/settings/rsharesettings.h" #include "util/RsUserdata.h" +#include "GxsStatisticsProvider.h" #include @@ -44,7 +45,7 @@ class UIStateHelper; struct RsGxsCommentService; class GxsCommentDialog; -class GxsGroupFrameDialog : public MainPage +class GxsGroupFrameDialog : public MainPage, public GxsStatisticsProvider { Q_OBJECT @@ -74,13 +75,13 @@ public: GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl, const QString& settings_name,QWidget *parent = 0,bool allow_dist_sync=false); virtual ~GxsGroupFrameDialog(); - bool navigate(const RsGxsGroupId &groupId, const RsGxsMessageId& msgId); + virtual bool navigate(const RsGxsGroupId &groupId, const RsGxsMessageId& msgId); virtual QString getHelpString() const =0; virtual void getGroupList(std::map &groups) ; - void getServiceStatistics(GxsServiceStatistic& stats) const ; + void getServiceStatistics(GxsServiceStatistic& stats) const override; static uint32_t checkDelay(uint32_t time_in_secs); @@ -99,17 +100,13 @@ protected: virtual void checkRequestGroup(const RsGxsGroupId& /* grpId */) {} // overload this one in order to retrieve full group data when the group is browsed void updateMessageSummaryList(RsGxsGroupId groupId); - void updateGroupStatistics(const RsGxsGroupId &groupId); virtual const std::set getSearchRequests() const { return std::set(); } // overload this for subclasses that provide distant search - // These two need to be overloaded by subsclasses, possibly calling the blocking API, since they are used asynchroneously. + // This needs to be overloaded by subsclasses, possibly calling the blocking API, since it is used asynchroneously. + virtual bool getGroupData(std::list& groupInfo) =0; - virtual bool getGroupData(std::list& groupInfo) =0; - virtual bool getGroupStatistics(const RsGxsGroupId& groupId,GxsGroupStatistic& stat) =0; - - void updateGroupStatisticsReal(const RsGxsGroupId &groupId); - void updateMessageSummaryListReal(RsGxsGroupId groupId); + void updateMessageSummaryListReal(RsGxsGroupId groupId); private slots: void todo(); @@ -154,7 +151,6 @@ private slots: private: virtual QString text(TextType type) = 0; virtual QString icon(IconType type) = 0; - virtual QString settingsGroupName() = 0; virtual TurtleRequestId distantSearch(const QString& search_string) ; virtual GxsGroupDialog *createNewGroupDialog() = 0; @@ -186,16 +182,31 @@ private: // subscribe/unsubscribe ack. - GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId); + virtual GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId); GxsMessageFrameWidget *createMessageWidget(const RsGxsGroupId &groupId); GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId); protected: + void updateSearchResults(const TurtleRequestId &sid); void updateSearchResults(); // update all searches + virtual void updateGroupStatistics(const RsGxsGroupId &groupId); + virtual void updateGroupStatisticsReal(const RsGxsGroupId &groupId); - bool mCountChildMsgs; // Count unread child messages? + // This needs to be overloaded by subclasses, possibly calling the blocking API, since it is used asynchronously. + virtual bool getGroupStatistics(const RsGxsGroupId& groupId, GxsGroupStatistic& stat) = 0; + + QString mSettingsName; + RsGxsIfaceHelper *mInterface; + bool mDistSyncAllowed; + std::map mCachedGroupStats; + bool mShouldUpdateGroupStatistics; + std::set mGroupStatisticsToUpdate; + bool mCountChildMsgs; + RsGxsGroupId mNavigatePendingGroupId; + RsGxsMessageId mNavigatePendingMsgId; + UIStateHelper *mStateHelper; private: GxsMessageFrameWidget *currentWidget() const; @@ -203,36 +214,26 @@ private: bool mInitialized; bool mInFill; - bool mDistSyncAllowed; - QString mSettingsName; + RsGxsGroupId mGroupId; - RsGxsIfaceHelper *mInterface; QTreeWidgetItem *mYourGroups; QTreeWidgetItem *mSubscribedGroups; QTreeWidgetItem *mPopularGroups; QTreeWidgetItem *mOtherGroups; - RsGxsGroupId mNavigatePendingGroupId; - RsGxsMessageId mNavigatePendingMsgId; - // Message summary list update bool mShouldUpdateMessageSummaryList ; // whether we should update the counting for groups. This takes some CPU so we only do it when needed. std::set mGroupIdsSummaryToUpdate; // GroupStatistics update - bool mShouldUpdateGroupStatistics; rstime_t mLastGroupStatisticsUpdateTs; - std::set mGroupStatisticsToUpdate; - - UIStateHelper *mStateHelper; /** Qt Designer generated object */ Ui::GxsGroupFrameDialog *ui; std::map mCachedGroupMetas; - std::map mCachedGroupStats; std::map mSearchGroupsItems ; std::map > mKnownGroups; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.ui b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.ui index 8cb249020..d73871619 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.ui +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.ui @@ -62,7 +62,7 @@ - :/images/konversation.png + :/images/konversation.png true @@ -74,7 +74,6 @@ 12 - 75 true diff --git a/retroshare-gui/src/gui/gxs/GxsStatisticsProvider.cpp b/retroshare-gui/src/gui/gxs/GxsStatisticsProvider.cpp new file mode 100644 index 000000000..47a77e983 --- /dev/null +++ b/retroshare-gui/src/gui/gxs/GxsStatisticsProvider.cpp @@ -0,0 +1,21 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxs/GxsStatisticsProvider.cpp * + * * + * Copyright 2012-2013 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "GxsStatisticsProvider.h" diff --git a/retroshare-gui/src/gui/gxs/GxsStatisticsProvider.h b/retroshare-gui/src/gui/gxs/GxsStatisticsProvider.h new file mode 100644 index 000000000..3309e795b --- /dev/null +++ b/retroshare-gui/src/gui/gxs/GxsStatisticsProvider.h @@ -0,0 +1,35 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxs/GxsStatisticsProvider.h * + * * + * Copyright 2012-2013 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef GXSSTATISTICSPROVIDER_H +#define GXSSTATISTICSPROVIDER_H + +#include "retroshare/rsgxsifacetypes.h" + +// Interface for GxsUserNotify to query service statistics. + +class GxsStatisticsProvider +{ +public: + virtual ~GxsStatisticsProvider() = default; + virtual void getServiceStatistics(GxsServiceStatistic& stats) const = 0; +}; + +#endif // GXSSTATISTICSPROVIDER_H diff --git a/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp b/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp index 1784a6f8c..143a06ae5 100644 --- a/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp +++ b/retroshare-gui/src/gui/gxs/GxsUserNotify.cpp @@ -26,7 +26,7 @@ #define TOKEN_TYPE_STATISTICS 1 -GxsUserNotify::GxsUserNotify(RsGxsIfaceHelper */*ifaceImpl*/, const GxsGroupFrameDialog *g,QObject *parent) : UserNotify(parent), mGroupFrameDialog(g) +GxsUserNotify::GxsUserNotify(RsGxsIfaceHelper */*ifaceImpl*/, const GxsStatisticsProvider *g,QObject *parent) : UserNotify(parent), mGxsStatisticsProvider(g) { mNewThreadMessageCount = 0; mNewChildMessageCount = 0; @@ -42,7 +42,7 @@ void GxsUserNotify::startUpdate() GxsServiceStatistic stats; - mGroupFrameDialog->getServiceStatistics(stats); + mGxsStatisticsProvider->getServiceStatistics(stats); /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information diff --git a/retroshare-gui/src/gui/gxs/GxsUserNotify.h b/retroshare-gui/src/gui/gxs/GxsUserNotify.h index 66890a1c3..27f5099e2 100644 --- a/retroshare-gui/src/gui/gxs/GxsUserNotify.h +++ b/retroshare-gui/src/gui/gxs/GxsUserNotify.h @@ -23,7 +23,7 @@ #include #include "gui/common/UserNotify.h" -#include "gui/gxs/GxsGroupFrameDialog.h" +#include "gui/gxs/GxsStatisticsProvider.h" class RsGxsIfaceHelper; class RsGxsUpdateBroadcastBase; @@ -33,7 +33,7 @@ class GxsUserNotify : public UserNotify Q_OBJECT public: - GxsUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); + GxsUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent = 0); virtual ~GxsUserNotify(); protected: @@ -46,7 +46,8 @@ protected: bool mCountChildMsgs; // Count new child messages? private: - const GxsGroupFrameDialog *mGroupFrameDialog; + + const GxsStatisticsProvider *mGxsStatisticsProvider; unsigned int mNewThreadMessageCount; unsigned int mNewChildMessageCount; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h index 285afaa9d..36fd73cdd 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.h @@ -71,7 +71,7 @@ private: /* GxsGroupFrameDialog */ virtual QString text(TextType type) override; virtual QString icon(IconType type) override; - virtual QString settingsGroupName() override { return "ChannelDialog"; } + virtual QString settingsGroupName() { return "ChannelDialog"; } virtual GxsGroupDialog *createNewGroupDialog() override; virtual GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) override; virtual int shareKeyType() override; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp index ce88231fc..46c5afaab 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.cpp @@ -23,7 +23,7 @@ #include "gui/MainWindow.h" #include "gui/common/FilesDefs.h" -GxsChannelUserNotify::GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent) : +GxsChannelUserNotify::GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent) : GxsUserNotify(ifaceImpl, g, parent) { } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h index 2604f5d34..94a787e91 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelUserNotify.h @@ -22,14 +22,13 @@ #define GXSCHANNELUSERNOTIFY_H #include "gui/gxs/GxsUserNotify.h" -#include "gui/gxs/GxsGroupFrameDialog.h" class GxsChannelUserNotify : public GxsUserNotify { Q_OBJECT public: - explicit GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); + explicit GxsChannelUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group) override; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp index b2e5bcc8e..ef890f350 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.cpp @@ -23,7 +23,7 @@ #include "GxsForumUserNotify.h" #include "gui/MainWindow.h" -GxsForumUserNotify::GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent) : +GxsForumUserNotify::GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent) : GxsUserNotify(ifaceImpl, g, parent) { mCountChildMsgs = true; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h index 4d681767f..69cca1960 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumUserNotify.h @@ -28,7 +28,7 @@ class GxsForumUserNotify : public GxsUserNotify Q_OBJECT public: - explicit GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsGroupFrameDialog *g, QObject *parent = 0); + explicit GxsForumUserNotify(RsGxsIfaceHelper *ifaceImpl, const GxsStatisticsProvider *g, QObject *parent = 0); virtual bool hasSetting(QString *name, QString *group) override; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h index 98297e712..d6451c779 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumsDialog.h @@ -53,7 +53,7 @@ private: /* GxsGroupFrameDialog */ QString text(TextType type)override ; QString icon(IconType type)override ; - QString settingsGroupName() override { return "ForumsDialog"; } + QString settingsGroupName() { return "ForumsDialog"; } GxsGroupDialog *createNewGroupDialog() override; GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) override; diff --git a/retroshare-gui/src/gui/icons.qrc b/retroshare-gui/src/gui/icons.qrc index 5d11f372b..b9b0aabce 100644 --- a/retroshare-gui/src/gui/icons.qrc +++ b/retroshare-gui/src/gui/icons.qrc @@ -356,7 +356,8 @@ icons/fullscreen-exit.png icons/notification.png icons/wire.png - icons/wire-circle.png + icons/png/wire-notify.png + icons/png/wire-circle.png icons/folder-account.svg icons/notification.svg icons/groups/blue.svg diff --git a/retroshare-gui/src/gui/icons/png/wire-circle.png b/retroshare-gui/src/gui/icons/png/wire-circle.png new file mode 100644 index 000000000..128159150 Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/wire-circle.png differ diff --git a/retroshare-gui/src/gui/icons/png/wire-notify.png b/retroshare-gui/src/gui/icons/png/wire-notify.png new file mode 100644 index 000000000..c241bf55f Binary files /dev/null and b/retroshare-gui/src/gui/icons/png/wire-notify.png differ diff --git a/retroshare-gui/src/gui/settings/NotifyPage.cpp b/retroshare-gui/src/gui/settings/NotifyPage.cpp index 189a93cc2..568804e24 100755 --- a/retroshare-gui/src/gui/settings/NotifyPage.cpp +++ b/retroshare-gui/src/gui/settings/NotifyPage.cpp @@ -167,6 +167,10 @@ NotifyPage::NotifyPage(QWidget * parent, Qt::WindowFlags flags) connect(ui.notify_Security, SIGNAL(toggled(bool)), this, SLOT(updateNewsFeedFlags())); connect(ui.notify_SecurityIp, SIGNAL(toggled(bool)), this, SLOT(updateNewsFeedFlags())); +#ifdef RS_USE_WIRE + connect(ui.notify_Wire, SIGNAL(toggled(bool)), this, SLOT(updateNewsFeedFlags())); +#endif + connect(ui.systray_ChatLobby, SIGNAL(toggled(bool)), this, SLOT(updateSystrayChatLobby())); connect(ui.systray_GroupChat, SIGNAL(toggled(bool)), this, SLOT(updateSystrayGroupChat())); @@ -207,6 +211,11 @@ RsFeedTypeFlags NotifyPage::getNewsFlags() if (ui.notify_SecurityIp->isChecked()) newsFlags |= RsFeedTypeFlags::RS_FEED_TYPE_SECURITY_IP; +#ifdef RS_USE_WIRE + if (ui.notify_Wire->isChecked()) + newsFlags |= RsFeedTypeFlags::RS_FEED_TYPE_WIRE; +#endif + return newsFlags; } @@ -312,8 +321,13 @@ void NotifyPage::load() whileBlocking(ui.notify_Forums )->setChecked(!!(newsflags & RsFeedTypeFlags::RS_FEED_TYPE_FORUM)); whileBlocking(ui.notify_Posted )->setChecked(!!(newsflags & RsFeedTypeFlags::RS_FEED_TYPE_POSTED)); #if 0 - whileBlocking(ui.notify_Blogs)->setChecked(newsflags & RS_FEED_TYPE_BLOG); + whileBlocking(ui.notify_Blogs)->setChecked(newsflags & RsFeedTypeFlags::RS_FEED_TYPE_BLOG); #endif + +#ifdef RS_USE_WIRE + whileBlocking(ui.notify_Wire)->setChecked(!!(newsflags & RsFeedTypeFlags::RS_FEED_TYPE_WIRE)); +#endif + whileBlocking(ui.notify_Chat )->setChecked(!!(newsflags & RsFeedTypeFlags::RS_FEED_TYPE_CHAT)); whileBlocking(ui.notify_Messages )->setChecked(!!(newsflags & RsFeedTypeFlags::RS_FEED_TYPE_MSG)); whileBlocking(ui.notify_Chat )->setChecked(!!(newsflags & RsFeedTypeFlags::RS_FEED_TYPE_CHAT)); diff --git a/retroshare-gui/src/gui/settings/NotifyPage.ui b/retroshare-gui/src/gui/settings/NotifyPage.ui index fba357bcf..6eda7d5a3 100755 --- a/retroshare-gui/src/gui/settings/NotifyPage.ui +++ b/retroshare-gui/src/gui/settings/NotifyPage.ui @@ -7,7 +7,7 @@ 0 0 569 - 512 + 526 @@ -86,6 +86,13 @@ + + + + Wire + + + @@ -471,6 +478,13 @@ + + + RSComboBox + QComboBox +
    gui/common/RSComboBox.h
    +
    +
    notify_Peers notify_Channels @@ -495,13 +509,6 @@ systray_ChatLobby pushButtonDisableAll - - - RSComboBox - QComboBox -
    gui/common/RSComboBox.h
    -
    -
    diff --git a/retroshare-gui/src/gui/settings/rsharesettings.cpp b/retroshare-gui/src/gui/settings/rsharesettings.cpp index 32d3a4764..2c1f28598 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.cpp +++ b/retroshare-gui/src/gui/settings/rsharesettings.cpp @@ -143,6 +143,10 @@ void RshareSettings::initSettings() RsNotifyPopupFlags defNotify = (RsNotifyPopupFlags::RS_POPUP_CONNECT | RsNotifyPopupFlags::RS_POPUP_MSG); RsFeedTypeFlags defNewsFeed = (RsFeedTypeFlags::RS_FEED_TYPE_MSG | RsFeedTypeFlags::RS_FEED_TYPE_FILES | RsFeedTypeFlags::RS_FEED_TYPE_SECURITY | RsFeedTypeFlags::RS_FEED_TYPE_SECURITY_IP | RsFeedTypeFlags::RS_FEED_TYPE_CIRCLE | RsFeedTypeFlags::RS_FEED_TYPE_CHANNEL |RsFeedTypeFlags::RS_FEED_TYPE_FORUM | RsFeedTypeFlags::RS_FEED_TYPE_POSTED); +#ifdef RS_USE_WIRE + defNewsFeed = (defNewsFeed | RsFeedTypeFlags::RS_FEED_TYPE_WIRE); +#endif + setDefault(SETTING_NEWSFEED_FLAGS, (int)defNewsFeed); setDefault(SETTING_CHAT_FLAGS, defChat); setDefault(SETTING_NOTIFY_FLAGS, (int)defNotify); @@ -1114,6 +1118,10 @@ static QString groupFrameSettingsTypeToString(GroupFrameSettings::Type type) return "Channel"; case GroupFrameSettings::Posted: return "Posted"; +#ifdef RS_USE_WIRE + case GroupFrameSettings::Wire: + return "Wire"; +#endif } return ""; diff --git a/retroshare-gui/src/gui/settings/rsharesettings.h b/retroshare-gui/src/gui/settings/rsharesettings.h index 3c87e6aaf..77c7e625e 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.h +++ b/retroshare-gui/src/gui/settings/rsharesettings.h @@ -44,7 +44,7 @@ class QMainWindow; class GroupFrameSettings { public: - enum Type { Nothing, Forum, Channel, Posted }; + enum Type { Nothing, Forum, Channel, Posted, Wire }; public: GroupFrameSettings() diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 96dde5400..b33ada159 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -117,7 +117,7 @@ CONFIG += gxscircles ## To enable unfinished services #CONFIG += wikipoos -#CONFIG += gxsthewire +CONFIG += gxsthewire #CONFIG += gxsphotoshare DEFINES += RS_RELEASE_VERSION @@ -1228,7 +1228,10 @@ gxsthewire { gui/TheWire/PulseReply.h \ gui/TheWire/PulseReplySeperator.h \ gui/TheWire/PulseMessage.h \ - gui/TheWire/CustomFrame.h \ + gui/TheWire/CustomFrame.h \ + gui/TheWire/WireUserNotify.h \ + gui/feeds/WireNotifyGroupItem.h \ + gui/feeds/WireNotifyPostItem.h \ FORMS += gui/TheWire/WireDialog.ui \ gui/TheWire/WireGroupItem.ui \ @@ -1239,7 +1242,9 @@ gxsthewire { gui/TheWire/PulseReply.ui \ gui/TheWire/PulseReplySeperator.ui \ gui/TheWire/PulseMessage.ui \ - + gui/feeds/WireNotifyGroupItem.ui \ + gui/feeds/WireNotifyPostItem.ui \ + SOURCES += gui/TheWire/WireDialog.cpp \ gui/TheWire/WireGroupItem.cpp \ gui/TheWire/WireGroupDialog.cpp \ @@ -1251,7 +1256,10 @@ gxsthewire { gui/TheWire/PulseReply.cpp \ gui/TheWire/PulseReplySeperator.cpp \ gui/TheWire/PulseMessage.cpp \ - gui/TheWire/CustomFrame.cpp \ + gui/TheWire/CustomFrame.cpp \ + gui/TheWire/WireUserNotify.cpp \ + gui/feeds/WireNotifyGroupItem.cpp \ + gui/feeds/WireNotifyPostItem.cpp \ RESOURCES += gui/TheWire/TheWire_images.qrc } @@ -1447,7 +1455,7 @@ gxsgui { # gui/gxs/GxsFeedWidget.h \ util/TokenQueue.h \ util/RsGxsUpdateBroadcast.h \ - + gui/gxs/GxsStatisticsProvider.h \ # gui/gxs/GxsMsgDialog.h \ FORMS += gui/gxs/GxsGroupDialog.ui \ @@ -1478,7 +1486,7 @@ gxsgui { # gui/gxs/GxsFeedWidget.cpp \ util/TokenQueue.cpp \ util/RsGxsUpdateBroadcast.cpp \ - + gui/gxs/GxsStatisticsProvider.cpp \ # gui/gxs/GxsMsgDialog.cpp \ diff --git a/retroshare.pri b/retroshare.pri index 983fda4e9..a92414f26 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -33,6 +33,9 @@ CONFIG *= retroshare_gui no_retroshare_gui:CONFIG -= retroshare_gui +# Enable TheWire - RsWire - is intended to be a Twitter clone +CONFIG *= gxsthewire + # Enable GXS distant syncronization CONFIG *= gxsdistsync