From 4cc96003c6702168da2faa955e3c771272211e32 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 23 Jul 2010 15:47:48 +0100 Subject: Recognise when leaving a MUC (disconnect or kick). Also cleans up some outstanding MUC issues. Resolves: #288 Resolves: #392 Resolves: #279 Resolves: #114 diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index f0d4b7a..61d0ab7 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -41,7 +41,7 @@ namespace Swift { void setAvailableServerFeatures(boost::shared_ptr info); void handleIncomingMessage(boost::shared_ptr message); void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional& label, const String& avatarPath, const boost::posix_time::ptime& time); - void setEnabled(bool enabled); + virtual void setEnabled(bool enabled); virtual void setToJID(const JID& jid) {toJID_ = jid;}; protected: ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController); diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 2ed7051..921cc6f 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -139,12 +139,18 @@ void ChatsManager::setServerDiscoInfo(boost::shared_ptr info) { } } +/** + * This is to be called on connect/disconnect. + */ void ChatsManager::setEnabled(bool enabled) { foreach (JIDChatControllerPair controllerPair, chatControllers_) { controllerPair.second->setEnabled(enabled); } foreach (JIDMUCControllerPair controllerPair, mucControllers_) { controllerPair.second->setEnabled(enabled); + if (enabled) { + controllerPair.second->rejoin(); + } } } @@ -201,9 +207,9 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) { void ChatsManager::handleJoinMUCRequest(const JID &muc, const boost::optional& nickMaybe) { std::map::iterator it = mucControllers_.find(muc); if (it != mucControllers_.end()) { - //FIXME: What's correct behaviour here? + it->second->rejoin(); } else { - String nick = nickMaybe ? nickMaybe.get() : "Swift user"; + String nick = nickMaybe ? nickMaybe.get() : jid_.getNode(); MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, presenceSender_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_); mucControllers_[muc] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 1e790b7..650a915 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -46,10 +46,9 @@ MUCController::MUCController ( bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController) : - ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController), - muc_(new MUC(stanzaChannel, presenceSender, muc)), - nick_(nick) { - parting_ = false; + ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController), muc_(new MUC(stanzaChannel, presenceSender, muc)), nick_(nick) { + parting_ = true; + joined_ = false; events_ = uiEventStream; roster_ = new Roster(true); @@ -69,11 +68,9 @@ MUCController::MUCController ( loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this)); loginCheckTimer_->start(); } - - muc_->joinAs(nick); chatWindow_->convertToMUC(); chatWindow_->addSystemMessage("Trying to join room " + toJID_.toString()); - joined_ = false; + rejoin(); if (avatarManager_ != NULL) { avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1, _2))); } @@ -90,6 +87,17 @@ MUCController::~MUCController() { delete completer_; } +/** + * Join the MUC if not already in it. + */ +void MUCController::rejoin() { + if (parting_) { + joined_ = false; + parting_ = false; + muc_->joinAs(nick_); + } +} + void MUCController::handleJoinTimeoutTick() { receivedActivity(); chatWindow_->addSystemMessage("Room " + toJID_.toString() + " is not responding. This operation may never complete"); @@ -122,7 +130,7 @@ void MUCController::handleJoinFailed(boost::shared_ptr error) { chatWindow_->addErrorMessage(errorMessage); if (!rejoinNick.isEmpty()) { nick_ = rejoinNick; - muc_->joinAs(rejoinNick); + rejoin(); } } @@ -132,6 +140,7 @@ void MUCController::handleJoinComplete(const String& nick) { String joinMessage = "You have joined room " + toJID_.toString() + " as " + nick; nick_ = nick; chatWindow_->addSystemMessage(joinMessage); + setEnabled(true); } void MUCController::handleAvatarChanged(const JID& jid, const String&) { @@ -194,7 +203,7 @@ bool MUCController::messageTargetsMe(boost::shared_ptr message) { void MUCController::preHandleIncomingMessage(boost::shared_ptr messageEvent) { boost::shared_ptr message = messageEvent->getStanza(); - if (messageTargetsMe(message)) { + if (joined_ && messageTargetsMe(message)) { eventController_->handleIncomingEvent(messageEvent); } String nick = message->getFrom().getResource(); @@ -235,15 +244,32 @@ String MUCController::roleToGroupName(MUCOccupant::Role role) { return result; } +void MUCController::setEnabled(bool enabled) { + ChatControllerBase::setEnabled(enabled); + if (!enabled) { + roster_->removeAll(); + /* handleUserLeft won't throw a part back up unless this is called + when it doesn't yet know we've left - which only happens on + disconnect, so call with disconnect here so if the signal does + bubble back up, it'll be with the right type.*/ + muc_->handleUserLeft(MUC::Disconnect); + } +} + void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& reason) { completer_->removeWord(occupant.getNick()); - String partMessage = occupant.getNick() + " has left the room"; + String partMessage = (occupant.getNick() != nick_) ? occupant.getNick() + " has left the room" : "You have left the room"; if (!reason.isEmpty()) { partMessage += " (" + reason + ")"; } partMessage += "."; chatWindow_->addSystemMessage(partMessage); - roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); + if (occupant.getNick() != nick_) { + roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); + } else { + parting_ = true; + setEnabled(false); + } } void MUCController::handleOccupantPresenceChange(boost::shared_ptr presence) { diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index 65ef84c..7d40ec2 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -35,6 +35,8 @@ namespace Swift { MUCController(const JID& self, const JID &muc, const String &nick, StanzaChannel* stanzaChannel, PresenceSender* presenceSender, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController); ~MUCController(); boost::signal onUserLeft; + virtual void setEnabled(bool enabled); + void rejoin(); protected: void preSendMessageRequest(boost::shared_ptr message); diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp index d705d34..c2d4147 100644 --- a/Swift/QtUI/Roster/RosterModel.cpp +++ b/Swift/QtUI/Roster/RosterModel.cpp @@ -177,7 +177,7 @@ QModelIndex RosterModel::index(RosterItem* item) const { /* Recursive check that it's ok to create such an item Assuming there are more contacts in a group than groups in a group, this could save a decent chunk of search time at startup.*/ - if (parent != roster_->getRoot() && !index(parent).isValid()) { + if (parent == NULL || (parent != roster_->getRoot() && !index(parent).isValid())) { return QModelIndex(); } for (size_t i = 0; i < parent->getDisplayedChildren().size(); i++) { diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp index b4265b5..91ba043 100644 --- a/Swiften/MUC/MUC.cpp +++ b/Swiften/MUC/MUC.cpp @@ -44,6 +44,18 @@ void MUC::part() { presenceSender->removeDirectedPresenceReceiver(ownMUCJID); } +void MUC::handleUserLeft(LeavingType type) { + std::map::iterator i = occupants.find(ownMUCJID.getResource()); + if (i != occupants.end()) { + MUCOccupant me = i->second; + occupants.erase(i); + onOccupantLeft(me, type, ""); + } + occupants.clear(); + joinComplete_ = false; + presenceSender->removeDirectedPresenceReceiver(ownMUCJID); +} + void MUC::handleIncomingPresence(boost::shared_ptr presence) { if (!isFromMUC(presence->getFrom())) { return; @@ -79,14 +91,18 @@ void MUC::handleIncomingPresence(boost::shared_ptr presence) { //170 is room logging to http //TODO: Nick changes if (presence->getType() == Presence::Unavailable) { - std::map::iterator i = occupants.find(nick); - if (i != occupants.end()) { - //TODO: part type - onOccupantLeft(i->second, Part, ""); - occupants.erase(i); + if (presence->getFrom() == ownMUCJID) { + handleUserLeft(Part); + return; + } else { + std::map::iterator i = occupants.find(nick); + if (i != occupants.end()) { + //TODO: part type + onOccupantLeft(i->second, Part, ""); + occupants.erase(i); + } } - } - else if (presence->getType() == Presence::Available) { + } else if (presence->getType() == Presence::Available) { std::map::iterator it = occupants.find(nick); MUCOccupant occupant(nick, role, affiliation); if (it != occupants.end()) { diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h index 40fc2f6..bccc26c 100644 --- a/Swiften/MUC/MUC.h +++ b/Swiften/MUC/MUC.h @@ -25,7 +25,7 @@ namespace Swift { class MUC { public: enum JoinResult { JoinSucceeded, JoinFailed }; - enum LeavingType { Part }; + enum LeavingType { Part, Disconnect }; public: MUC(StanzaChannel* stanzaChannel, PresenceSender* presenceSender, const JID &muc); @@ -36,6 +36,8 @@ namespace Swift { String getCurrentNick(); void part(); void handleIncomingMessage(boost::shared_ptr message); + /** Expose public so it can be called when e.g. user goes offline */ + void handleUserLeft(LeavingType); public: boost::signal onJoinComplete; diff --git a/Swiften/Roster/GroupRosterItem.cpp b/Swiften/Roster/GroupRosterItem.cpp index e5490e2..8e4be35 100644 --- a/Swiften/Roster/GroupRosterItem.cpp +++ b/Swiften/Roster/GroupRosterItem.cpp @@ -55,6 +55,27 @@ void GroupRosterItem::addChild(RosterItem* item) { } /** + * Does not emit a changed signal. + */ +void GroupRosterItem::removeAll() { + std::vector::iterator it = children_.begin(); + displayedChildren_.clear(); + while (it != children_.end()) { + ContactRosterItem* contact = dynamic_cast(*it); + if (contact) { + delete contact; + } + GroupRosterItem* group = dynamic_cast(*it); + if (group) { + group->removeAll(); + delete group; + } + it++; + } + children_.clear(); +} + +/** * Returns the removed item - but only if it's the only one, otherwise * the return result is undefined. */ diff --git a/Swiften/Roster/GroupRosterItem.h b/Swiften/Roster/GroupRosterItem.h index aca2b05..096d053 100644 --- a/Swiften/Roster/GroupRosterItem.h +++ b/Swiften/Roster/GroupRosterItem.h @@ -22,6 +22,7 @@ class GroupRosterItem : public RosterItem { const std::vector& getDisplayedChildren() const; void addChild(RosterItem* item); ContactRosterItem* removeChild(const JID& jid); + void removeAll(); void setDisplayed(RosterItem* item, bool displayed); boost::signal onChildrenChanged; static bool itemLessThan(const RosterItem* left, const RosterItem* right); diff --git a/Swiften/Roster/Roster.cpp b/Swiften/Roster/Roster.cpp index b7ed6c4..e91b843 100644 --- a/Swiften/Roster/Roster.cpp +++ b/Swiften/Roster/Roster.cpp @@ -82,6 +82,13 @@ struct JIDEqualsTo { JID jid; }; +void Roster::removeAll() { + root_->removeAll(); + itemMap_.clear(); + onChildrenChanged(root_); + onDataChanged(root_); +} + void Roster::removeContact(const JID& jid) { std::vector* items = &itemMap_[fullJIDMapping_ ? jid : jid.toBare()]; items->erase(std::remove_if(items->begin(), items->end(), JIDEqualsTo(jid)), items->end()); diff --git a/Swiften/Roster/Roster.h b/Swiften/Roster/Roster.h index 346157e..d54a12e 100644 --- a/Swiften/Roster/Roster.h +++ b/Swiften/Roster/Roster.h @@ -31,6 +31,7 @@ class Roster { void addContact(const JID& jid, const JID& displayJID, const String& name, const String& group); void removeContact(const JID& jid); void removeContactFromGroup(const JID& jid, const String& group); + void removeAll(); void applyOnItems(const RosterItemOperation& operation); void applyOnAllItems(const RosterItemOperation& operation); void applyOnItem(const RosterItemOperation& operation, const JID& jid); -- cgit v0.10.2-6-g49f6