summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-11-18 12:09:08 (GMT)
committerKevin Smith <git@kismith.co.uk>2010-12-22 20:19:07 (GMT)
commit9f04a7ec3429303118f12607703b877d8ba43888 (patch)
tree3e868395fa3c4f9cbbf84614094bb0251b425c15 /Swift/Controllers/Chat
parent04b99ce8430109d0467c29c85cb6bacb85c54c44 (diff)
downloadswift-9f04a7ec3429303118f12607703b877d8ba43888.zip
swift-9f04a7ec3429303118f12607703b877d8ba43888.tar.bz2
Basic User Search support, and Find Rooms cleanup.
Adds a throbber to the MUC search, turns the Add Contact dialog into something searchy, adds the option to open chats to arbitrary JIDs. Resolves: #614 Resolves: #695 Resolves: #436 Release-Notes: On servers that support it, users can now perform searches for contacts to add or chat to.
Diffstat (limited to 'Swift/Controllers/Chat')
-rw-r--r--Swift/Controllers/Chat/MUCSearchController.cpp144
-rw-r--r--Swift/Controllers/Chat/MUCSearchController.h14
-rw-r--r--Swift/Controllers/Chat/UserSearchController.cpp122
-rw-r--r--Swift/Controllers/Chat/UserSearchController.h59
4 files changed, 254 insertions, 85 deletions
diff --git a/Swift/Controllers/Chat/MUCSearchController.cpp b/Swift/Controllers/Chat/MUCSearchController.cpp
index 7368dbb..c254e51 100644
--- a/Swift/Controllers/Chat/MUCSearchController.cpp
+++ b/Swift/Controllers/Chat/MUCSearchController.cpp
@@ -11,22 +11,26 @@
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
-#include "Swiften/Disco/GetDiscoInfoRequest.h"
-#include "Swiften/Disco/GetDiscoItemsRequest.h"
+#include <Swiften/Disco/GetDiscoInfoRequest.h>
+#include <Swiften/Disco/GetDiscoItemsRequest.h>
-#include "Swift/Controllers/UIEvents/UIEventStream.h"
-#include "Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h"
-#include "Swift/Controllers/UIInterfaces/MUCSearchWindow.h"
-#include "Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h"
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/RequestMUCSearchUIEvent.h>
+#include <Swift/Controllers/UIInterfaces/MUCSearchWindow.h>
+#include <Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h>
+#include <Swift/Controllers/DiscoServiceWalker.h>
+#include <Swiften/Client/NickResolver.h>
namespace Swift {
static const String SEARCHED_SERVICES = "searchedServices";
-MUCSearchController::MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* factory, IQRouter* iqRouter, SettingsProvider* settings) : jid_(jid) {
+MUCSearchController::MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* factory, IQRouter* iqRouter, SettingsProvider* settings, NickResolver *nickResolver) : jid_(jid) {
iqRouter_ = iqRouter;
settings_ = settings;
uiEventStream_ = uiEventStream;
+ nickResolver_ = nickResolver;
+ itemsInProgress_ = 0;
uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&MUCSearchController::handleUIEvent, this, _1));
window_ = NULL;
factory_ = factory;
@@ -34,6 +38,9 @@ MUCSearchController::MUCSearchController(const JID& jid, UIEventStream* uiEventS
}
MUCSearchController::~MUCSearchController() {
+ foreach (DiscoServiceWalker* walker, walksInProgress_) {
+ delete walker;
+ }
delete window_;
}
@@ -42,12 +49,12 @@ void MUCSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
if (searchEvent) {
if (!window_) {
window_ = factory_->createMUCSearchWindow(uiEventStream_);
- window_->onAddService.connect(boost::bind(&MUCSearchController::handleAddService, this, _1, true));
+ window_->onAddService.connect(boost::bind(&MUCSearchController::handleAddService, this, _1));
window_->addSavedServices(savedServices_);
- handleAddService(JID(jid_.getDomain()), true);
+ handleAddService(JID(jid_.getDomain()));
}
window_->setMUC("");
- window_->setNick(jid_.getNode());
+ window_->setNick(nickResolver_->jidToNick(jid_));
window_->show();
return;
}
@@ -79,76 +86,66 @@ void MUCSearchController::addAndSaveServices(const JID& jid) {
window_->addSavedServices(savedServices_);
}
-void MUCSearchController::handleAddService(const JID& jid, bool userTriggered) {
- if (userTriggered) {
- addAndSaveServices(jid);
- }
- if (std::find(services_.begin(), services_.end(), jid) != services_.end()) {
- if (!userTriggered) {
- /* No infinite recursion. (Some buggy servers do infinitely deep disco of themselves)*/
- return;
- }
- } else if (userTriggered) {
- services_.push_back(jid);
- serviceDetails_[jid].setComplete(false);
- refreshView();
- }
+void MUCSearchController::handleAddService(const JID& jid) {
if (!jid.isValid()) {
//Set Window to say error this isn't valid
return;
}
- GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(jid, iqRouter_);
- discoInfoRequest->onResponse.connect(boost::bind(&MUCSearchController::handleDiscoInfoResponse, this, _1, _2, jid));
- discoInfoRequest->send();
-}
-
-void MUCSearchController::removeService(const JID& jid) {
- serviceDetails_.erase(jid);
- services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end());
+ addAndSaveServices(jid);
+ services_.push_back(jid);
+ serviceDetails_[jid].setComplete(false);
+ window_->setSearchInProgress(true);
refreshView();
+ DiscoServiceWalker* walker = new DiscoServiceWalker(jid, iqRouter_);
+ walker->onServiceFound.connect(boost::bind(&MUCSearchController::handleDiscoServiceFound, this, _1, _2));
+ walker->onWalkComplete.connect(boost::bind(&MUCSearchController::handleDiscoWalkFinished, this, walker));
+ walksInProgress_.push_back(walker);
+ walker->beginWalk();
}
-void MUCSearchController::handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error, const JID& jid) {
- if (error) {
- handleDiscoError(jid, error);
- return;
- }
- GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(jid, iqRouter_);
- bool mucService = false;
- bool couldContainServices = false;
+void MUCSearchController::handleDiscoServiceFound(const JID& jid, boost::shared_ptr<DiscoInfo> info) {
+ bool isMUC;
String name;
foreach (DiscoInfo::Identity identity, info->getIdentities()) {
- if ((identity.getCategory() == "directory"
- && identity.getType() == "chatroom")
- || (identity.getCategory() == "conference"
- && identity.getType() == "text")) {
- mucService = true;
- name = identity.getName();
- }
- if (identity.getCategory() == "server") {
- couldContainServices = true;
- name = identity.getName();
- }
+ if ((identity.getCategory() == "directory"
+ && identity.getType() == "chatroom")
+ || (identity.getCategory() == "conference"
+ && identity.getType() == "text")) {
+ isMUC = true;
+ name = identity.getName();
+ }
}
- services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end()); /* Bring it back to the end on a refresh */
- services_.push_back(jid);
- serviceDetails_[jid].setName(name);
- serviceDetails_[jid].setJID(jid);
- serviceDetails_[jid].setComplete(false);
-
- if (mucService) {
+ if (isMUC) {
+ services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end()); /* Bring it back to the end on a refresh */
+ services_.push_back(jid);
+ serviceDetails_[jid].setName(name);
+ serviceDetails_[jid].setJID(jid);
+ serviceDetails_[jid].setComplete(false);
+ itemsInProgress_++;
+ GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(jid, iqRouter_);
discoItemsRequest->onResponse.connect(boost::bind(&MUCSearchController::handleRoomsItemsResponse, this, _1, _2, jid));
- } else if (couldContainServices) {
- discoItemsRequest->onResponse.connect(boost::bind(&MUCSearchController::handleServerItemsResponse, this, _1, _2, jid));
+ discoItemsRequest->send();
} else {
removeService(jid);
- return;
}
- discoItemsRequest->send();
+ refreshView();
+}
+
+void MUCSearchController::handleDiscoWalkFinished(DiscoServiceWalker* walker) {
+ walksInProgress_.erase(std::remove(walksInProgress_.begin(), walksInProgress_.end(), walker), walksInProgress_.end());
+ updateInProgressness();
+ delete walker;
+}
+
+void MUCSearchController::removeService(const JID& jid) {
+ serviceDetails_.erase(jid);
+ services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end());
refreshView();
}
void MUCSearchController::handleRoomsItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, const JID& jid) {
+ itemsInProgress_--;
+ updateInProgressness();
if (error) {
handleDiscoError(jid, error);
return;
@@ -161,25 +158,6 @@ void MUCSearchController::handleRoomsItemsResponse(boost::shared_ptr<DiscoItems>
refreshView();
}
-void MUCSearchController::handleServerItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, const JID& jid) {
- if (error) {
- handleDiscoError(jid, error);
- return;
- }
- if (jid.isValid()) {
- removeService(jid);
- }
- foreach (DiscoItems::Item item, items->getItems()) {
- if (item.getNode().isEmpty()) {
- /* Don't look at noded items. It's possible that this will exclude some services,
- * but I've never seen one in the wild, and it's an easy fix for not looping.
- */
- handleAddService(item.getJID());
- }
- }
- refreshView();
-}
-
void MUCSearchController::handleDiscoError(const JID& jid, ErrorPayload::ref error) {
serviceDetails_[jid].setComplete(true);
serviceDetails_[jid].setError(error->getText());
@@ -192,4 +170,8 @@ void MUCSearchController::refreshView() {
}
}
+void MUCSearchController::updateInProgressness() {
+ window_->setSearchInProgress(walksInProgress_.size() + itemsInProgress_ > 0);
+}
+
}
diff --git a/Swift/Controllers/Chat/MUCSearchController.h b/Swift/Controllers/Chat/MUCSearchController.h
index f09a801..6caee54 100644
--- a/Swift/Controllers/Chat/MUCSearchController.h
+++ b/Swift/Controllers/Chat/MUCSearchController.h
@@ -27,6 +27,8 @@ namespace Swift {
class MUCSearchWindow;
class MUCSearchWindowFactory;
class IQRouter;
+ class DiscoServiceWalker;
+ class NickResolver;
class MUCService {
public:
@@ -86,28 +88,32 @@ namespace Swift {
class MUCSearchController {
public:
- MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, SettingsProvider* settings);
+ MUCSearchController(const JID& jid, UIEventStream* uiEventStream, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, SettingsProvider* settings, NickResolver* nickResolver);
~MUCSearchController();
private:
void handleUIEvent(boost::shared_ptr<UIEvent> event);
- void handleAddService(const JID& jid, bool userTriggered=false);
- void handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error, const JID& jid);
+ void handleAddService(const JID& jid);
void handleRoomsItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, const JID& jid);
- void handleServerItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, const JID& jid);
void handleDiscoError(const JID& jid, ErrorPayload::ref error);
+ void handleDiscoServiceFound(const JID&, boost::shared_ptr<DiscoInfo>);
+ void handleDiscoWalkFinished(DiscoServiceWalker* walker);
void removeService(const JID& jid);
void refreshView();
void loadServices();
void addAndSaveServices(const JID& jid);
+ void updateInProgressness();
UIEventStream* uiEventStream_;
MUCSearchWindow* window_;
MUCSearchWindowFactory* factory_;
SettingsProvider* settings_;
+ NickResolver* nickResolver_;
boost::bsignals::scoped_connection uiEventConnection_;
std::vector<JID> services_;
std::vector<JID> savedServices_;
std::map<JID, MUCService> serviceDetails_;
+ std::vector<DiscoServiceWalker*> walksInProgress_;
IQRouter* iqRouter_;
JID jid_;
+ int itemsInProgress_;
};
}
diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp
new file mode 100644
index 0000000..c3e40c8
--- /dev/null
+++ b/Swift/Controllers/Chat/UserSearchController.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swift/Controllers/Chat/UserSearchController.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Disco/GetDiscoInfoRequest.h>
+#include <Swiften/Disco/GetDiscoItemsRequest.h>
+
+#include <Swift/Controllers/DiscoServiceWalker.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/RequestUserSearchUIEvent.h>
+#include <Swift/Controllers/UIInterfaces/UserSearchWindow.h>
+#include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h>
+
+namespace Swift {
+UserSearchController::UserSearchController(const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* factory, IQRouter* iqRouter) : jid_(jid) {
+ iqRouter_ = iqRouter;
+ uiEventStream_ = uiEventStream;
+ uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&UserSearchController::handleUIEvent, this, _1));
+ window_ = NULL;
+ factory_ = factory;
+ discoWalker_ = NULL;
+}
+
+UserSearchController::~UserSearchController() {
+ delete window_;
+ delete discoWalker_;
+}
+
+void UserSearchController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
+ boost::shared_ptr<RequestUserSearchUIEvent> searchEvent = boost::dynamic_pointer_cast<RequestUserSearchUIEvent>(event);
+ if (searchEvent) {
+ if (!window_) {
+ window_ = factory_->createUserSearchWindow(uiEventStream_);
+ window_->onFormRequested.connect(boost::bind(&UserSearchController::handleFormRequested, this, _1));
+ window_->onSearchRequested.connect(boost::bind(&UserSearchController::handleSearch, this, _1, _2));
+ window_->setSelectedService(JID(jid_.getDomain()));
+ window_->clear();
+ }
+ window_->show();
+ return;
+ }
+}
+
+void UserSearchController::handleFormRequested(const JID& service) {
+ window_->setSearchError(false);
+ window_->setServerSupportsSearch(true);
+ //Abort a previous search if is active
+ delete discoWalker_;
+ discoWalker_ = new DiscoServiceWalker(service, iqRouter_);
+ discoWalker_->onServiceFound.connect(boost::bind(&UserSearchController::handleDiscoServiceFound, this, _1, _2, discoWalker_));
+ discoWalker_->onWalkComplete.connect(boost::bind(&UserSearchController::handleDiscoWalkFinished, this, discoWalker_));
+ discoWalker_->beginWalk();
+}
+
+void UserSearchController::handleDiscoServiceFound(const JID& jid, boost::shared_ptr<DiscoInfo> info, DiscoServiceWalker* walker) {
+ bool isUserDirectory = false;
+ bool supports55 = false;
+ foreach (DiscoInfo::Identity identity, info->getIdentities()) {
+ if ((identity.getCategory() == "directory"
+ && identity.getType() == "user")) {
+ isUserDirectory = true;
+ }
+ }
+ std::vector<String> features = info->getFeatures();
+ supports55 = std::find(features.begin(), features.end(), DiscoInfo::JabberSearchFeature) != features.end();
+ if (/*isUserDirectory && */supports55) { //FIXME: once M-Link correctly advertises directoryness.
+ /* Abort further searches.*/
+ delete discoWalker_;
+ discoWalker_ = NULL;
+ boost::shared_ptr<GenericRequest<SearchPayload> > searchRequest(new GenericRequest<SearchPayload>(IQ::Get, jid, boost::shared_ptr<SearchPayload>(new SearchPayload()), iqRouter_));
+ searchRequest->onResponse.connect(boost::bind(&UserSearchController::handleFormResponse, this, _1, _2, jid));
+ searchRequest->send();
+ }
+}
+
+void UserSearchController::handleFormResponse(boost::shared_ptr<SearchPayload> fields, ErrorPayload::ref error, const JID& jid) {
+ if (error || !fields) {
+ window_->setServerSupportsSearch(false);
+ return;
+ }
+ window_->setSearchFields(fields);
+}
+
+void UserSearchController::handleSearch(boost::shared_ptr<SearchPayload> fields, const JID& jid) {
+ boost::shared_ptr<GenericRequest<SearchPayload> > searchRequest(new GenericRequest<SearchPayload>(IQ::Set, jid, fields, iqRouter_));
+ searchRequest->onResponse.connect(boost::bind(&UserSearchController::handleSearchResponse, this, _1, _2, jid));
+ searchRequest->send();
+}
+
+void UserSearchController::handleSearchResponse(boost::shared_ptr<SearchPayload> resultsPayload, ErrorPayload::ref error, const JID& jid) {
+ if (error || !resultsPayload) {
+ window_->setSearchError(true);
+ return;
+ }
+ std::vector<UserSearchResult> results;
+ foreach (SearchPayload::Item item, resultsPayload->getItems()) {
+ JID jid(item.jid);
+ std::map<String, String> fields;
+ fields["first"] = item.first;
+ fields["last"] = item.last;
+ fields["nick"] = item.nick;
+ fields["email"] = item.email;
+ UserSearchResult result(jid, fields);
+ results.push_back(result);
+ }
+ window_->setResults(results);
+}
+
+void UserSearchController::handleDiscoWalkFinished(DiscoServiceWalker* walker) {
+ window_->setServerSupportsSearch(false);
+ delete discoWalker_;
+ discoWalker_ = NULL;
+}
+
+}
diff --git a/Swift/Controllers/Chat/UserSearchController.h b/Swift/Controllers/Chat/UserSearchController.h
new file mode 100644
index 0000000..3ba3352
--- /dev/null
+++ b/Swift/Controllers/Chat/UserSearchController.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <vector>
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/Elements/SearchPayload.h>
+#include <Swiften/Base/String.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/DiscoInfo.h>
+#include <Swiften/Elements/DiscoItems.h>
+#include <Swiften/Elements/ErrorPayload.h>
+
+namespace Swift {
+ class UIEventStream;
+ class UIEvent;
+ class UserSearchWindow;
+ class UserSearchWindowFactory;
+ class IQRouter;
+ class DiscoServiceWalker;
+
+ class UserSearchResult {
+ public:
+ UserSearchResult(const JID& jid, const std::map<String, String>& fields) : jid_(jid), fields_(fields) {}
+ const JID& getJID() const {return jid_;}
+ const std::map<String, String>& getFields() const {return fields_;}
+ private:
+ JID jid_;
+ std::map<String, String> fields_;
+ };
+
+ class UserSearchController {
+ public:
+ UserSearchController(const JID& jid, UIEventStream* uiEventStream, UserSearchWindowFactory* userSearchWindowFactory, IQRouter* iqRouter);
+ ~UserSearchController();
+ private:
+ void handleUIEvent(boost::shared_ptr<UIEvent> event);
+ void handleFormRequested(const JID& service);
+ void handleDiscoServiceFound(const JID& jid, boost::shared_ptr<DiscoInfo> info, DiscoServiceWalker* walker);
+ void handleDiscoWalkFinished(DiscoServiceWalker* walker);
+ void handleFormResponse(boost::shared_ptr<SearchPayload> items, ErrorPayload::ref error, const JID& jid);
+ void handleSearch(boost::shared_ptr<SearchPayload> fields, const JID& jid);
+ void handleSearchResponse(boost::shared_ptr<SearchPayload> results, ErrorPayload::ref error, const JID& jid);
+ UIEventStream* uiEventStream_;
+ UserSearchWindow* window_;
+ UserSearchWindowFactory* factory_;
+ boost::bsignals::scoped_connection uiEventConnection_;
+ IQRouter* iqRouter_;
+ JID jid_;
+ DiscoServiceWalker* discoWalker_;
+ };
+}