diff options
Diffstat (limited to 'Swift')
-rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 4 | ||||
-rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 30 | ||||
-rw-r--r-- | Swift/Controllers/Chat/MUCController.h | 3 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp | 17 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp | 7 |
5 files changed, 33 insertions, 28 deletions
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 9db343f..d9f80ba 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -848,60 +848,64 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti MUCController* controller = nullptr; SingleChatWindowFactoryAdapter* chatWindowFactoryAdapter = nullptr; if (reuseChatwindow) { chatWindowFactoryAdapter = new SingleChatWindowFactoryAdapter(reuseChatwindow); } std::shared_ptr<ChatMessageParser> chatMessageParser = std::make_shared<ChatMessageParser>(emoticons_, highlightManager_->getConfiguration(), ChatMessageParser::Mode::GroupChat); /* a message parser that knows this is a room/MUC (not a chat) */ controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, reuseChatwindow ? chatWindowFactoryAdapter : chatWindowFactory_, nickResolver_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_, chatMessageParser, isImpromptu, autoAcceptMUCInviteDecider_, vcardManager_, mucBookmarkManager_); if (chatWindowFactoryAdapter) { /* The adapters are only passed to chat windows, which are deleted in their * controllers' dtor, which are deleted in ChatManager's dtor. The adapters * are also deleted there.*/ chatWindowFactoryAdapters_[controller] = chatWindowFactoryAdapter; } mucControllers_[mucJID] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller)); controller->onUserJoined.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), "", true)); controller->onUserNicknameChanged.connect(boost::bind(&ChatsManager::handleUserNicknameChanged, this, controller, _1, _2)); controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), _1, true)); controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); if (!stanzaChannel_->isAvailable()) { /* When online, the MUC is added to the registry in MUCImpl::internalJoin. This method is not * called when Swift is offline, so we add it here as only MUCs in the registry are rejoined * when going back online. */ mucRegistry_->addMUC(mucJID.toBare()); } handleChatActivity(mucJID.toBare(), "", true); } + auto chatListWindowIter = std::find_if(recentChats_.begin(), recentChats_.end(), [&](const ChatListWindow::Chat& chatListWindow) { return mucJID == (chatListWindow.jid); }); + if (chatListWindowIter != recentChats_.end()) { + mucControllers_[mucJID]->setChatWindowTitle(chatListWindowIter->getTitle()); + } mucControllers_[mucJID]->showChatWindow(); return muc; } void ChatsManager::handleSearchMUCRequest() { mucSearchController_->openSearchWindow(); } void ChatsManager::handleUserNicknameChanged(MUCController* mucController, const std::string& oldNickname, const std::string& newNickname) { JID oldMUCChatJID = mucController->getToJID().withResource(oldNickname); JID newMUCChatJID = mucController->getToJID().withResource(newNickname); SWIFT_LOG(debug) << "nickname change in " << mucController->getToJID().toString() << " from " << oldNickname << " to " << newNickname << std::endl; // get current chat controller ChatController *chatController = getChatControllerIfExists(oldMUCChatJID); if (chatController) { // adjust chat controller chatController->setToJID(newMUCChatJID); nickResolver_->onNickChanged(newMUCChatJID, oldNickname); chatControllers_.erase(oldMUCChatJID); chatControllers_[newMUCChatJID] = chatController; chatController->onActivity.disconnect(boost::bind(&ChatsManager::handleChatActivity, this, oldMUCChatJID, _1, false)); chatController->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, newMUCChatJID, _1, false)); } } bool ChatsManager::messageCausesSessionBinding(std::shared_ptr<Message> message) { diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index fd3dc37..d10e6d4 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -83,60 +83,61 @@ MUCController::MUCController ( ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* uiEventStream, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* xmppRoster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager, MUCBookmarkManager* mucBookmarkManager) : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), nickResolver, presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false), clientBlockListManager_(clientBlockListManager), mucBookmarkManager_(mucBookmarkManager) { assert(avatarManager_); parting_ = true; joined_ = false; lastWasPresence_ = false; shouldJoinOnReconnect_ = true; doneGettingHistory_ = false; xmppRoster_ = xmppRoster; subject_ = ""; isInitialJoin_ = true; + chatWindowTitle_ = ""; roster_ = std::unique_ptr<Roster>(new Roster(false, true)); rosterVCardProvider_ = new RosterVCardProvider(roster_.get(), vcardManager, JID::WithResource); completer_ = new TabComplete(); chatWindow_->setRosterModel(roster_.get()); chatWindow_->setTabComplete(completer_); chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this)); chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1)); chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2)); chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1)); chatWindow_->onBookmarkRequest.connect(boost::bind(&MUCController::handleBookmarkRequest, this)); chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1)); chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this)); chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this)); chatWindow_->onInviteToChat.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1)); chatWindow_->onGetAffiliationsRequest.connect(boost::bind(&MUCController::handleGetAffiliationsRequest, this)); chatWindow_->onChangeAffiliationsRequest.connect(boost::bind(&MUCController::handleChangeAffiliationsRequest, this, _1)); chatWindow_->onUnblockUserRequest.connect(boost::bind(&MUCController::handleUnblockUserRequest, this)); chatWindow_->onContinuationsBroken.connect(boost::bind(&MUCController::addChatSystemMessage, this)); muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1)); muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1)); muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1)); muc_->onOccupantNicknameChanged.connect(boost::bind(&MUCController::handleOccupantNicknameChanged, this, _1, _2)); muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1)); muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3)); muc_->onRoleChangeFailed.connect(boost::bind(&MUCController::handleOccupantRoleChangeFailed, this, _1, _2, _3)); muc_->onAffiliationListReceived.connect(boost::bind(&MUCController::handleAffiliationListReceived, this, _1, _2)); muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1)); muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1)); chatMessageParser_->setNick(nick_); @@ -379,119 +380,114 @@ void MUCController::handleJoinFailed(std::shared_ptr<ErrorPayload> error) { #pragma clang diagnostic pop void MUCController::handleJoinComplete(const std::string& nick) { receivedActivity(); renameCounter_ = 0; joined_ = true; if (isImpromptu_) { lastStartMessage_ = str(format(QT_TRANSLATE_NOOP("", "You have joined the chat as %1%.")) % nick); } else { lastStartMessage_ = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick); } setNick(nick); chatWindow_->replaceSystemMessage(chatMessageParser_->parseMessageBody(lastStartMessage_, "", true), lastJoinMessageUID_, ChatWindow::UpdateTimestamp); lastJoinMessageUID_ = ""; #ifdef SWIFT_EXPERIMENTAL_HISTORY addRecentLogs(); #endif clearPresenceQueue(); shouldJoinOnReconnect_ = true; setEnabled(true); if (isImpromptu_) { setAvailableRoomActions(MUCOccupant::NoAffiliation, MUCOccupant::Participant); } else { MUCOccupant occupant = muc_->getOccupant(nick); setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole()); } onUserJoined(); - - if (isImpromptu_) { - setImpromptuWindowTitle(); - } } void MUCController::handleAvatarChanged(const JID& jid) { if (parting_ || !jid.equals(toJID_, JID::WithoutResource)) { return; } roster_->applyOnItems(SetAvatar(jid, avatarManager_->getAvatarPath(jid), JID::WithResource)); } void MUCController::handleWindowClosed() { parting_ = true; shouldJoinOnReconnect_ = false; muc_->part(); onUserLeft(); } void MUCController::handleOccupantJoined(const MUCOccupant& occupant) { if (nick_ != occupant.getNick()) { completer_->addWord(occupant.getNick()); } receivedActivity(); JID jid(nickToJID(occupant.getNick())); JID realJID; if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } currentOccupants_.insert(occupant.getNick()); NickJoinPart event(occupant.getNick(), Join); appendToJoinParts(joinParts_, event); MUCOccupant::Role role = MUCOccupant::Participant; MUCOccupant::Affiliation affiliation = MUCOccupant::NoAffiliation; if (!isImpromptu_) { role = occupant.getRole(); affiliation = occupant.getAffiliation(); } std::string groupName(roleToGroupName(role)); roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid)); roster_->applyOnItems(SetMUC(jid, role, affiliation)); roster_->getGroup(groupName)->setManualSort(roleToSortName(role)); if (joined_) { std::string joinString; if (role != MUCOccupant::NoRole && role != MUCOccupant::Participant) { joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the %3% as a %2%.")) % occupant.getNick() % roleToFriendlyName(role) % (isImpromptu_ ? QT_TRANSLATE_NOOP("", "chat") : QT_TRANSLATE_NOOP("", "room"))); } else { joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the %2%.")) % occupant.getNick() % (isImpromptu_ ? QT_TRANSLATE_NOOP("", "chat") : QT_TRANSLATE_NOOP("", "room"))); } if (shouldUpdateJoinParts()) { updateJoinParts(); } else { addPresenceMessage(joinString); } if (isImpromptu_) { - setImpromptuWindowTitle(); onActivity(""); } } handleAvatarChanged(jid); } void MUCController::addPresenceMessage(const std::string& message) { lastWasPresence_ = true; chatWindow_->addPresenceMessage(chatMessageParser_->parseMessageBody(message), ChatWindow::DefaultDirection); } void MUCController::setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role) { std::vector<ChatWindow::RoomAction> actions; if (role <= MUCOccupant::Participant) { actions.push_back(ChatWindow::ChangeSubject); } if (affiliation == MUCOccupant::Owner) { actions.push_back(ChatWindow::Configure); } if (affiliation <= MUCOccupant::Admin) { actions.push_back(ChatWindow::Affiliations); } if (affiliation == MUCOccupant::Owner) { actions.push_back(ChatWindow::Destroy); } if (role <= MUCOccupant::Visitor) { actions.push_back(ChatWindow::Invite); @@ -742,64 +738,60 @@ void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::Leaving else { switch (type) { case MUC::LeaveKick: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You have been kicked out of the room"); break; case MUC::LeaveBan: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You have been banned from the room"); break; case MUC::LeaveNotMember: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You are no longer a member of the room and have been removed"); break; case MUC::LeaveDestroy: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "The room has been destroyed"); break; case MUC::Disconnect: case MUC::LeavePart: partMessage = QT_TRANSLATE_NOOP("", "You have left the room"); } } if (!reason.empty()) { partMessage += " (" + reason + ")"; } partMessage += "."; if (occupant.getNick() != nick_) { if (shouldUpdateJoinParts()) { updateJoinParts(); } else { addPresenceMessage(partMessage); } roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); } else { addPresenceMessage(partMessage); parting_ = true; processUserPart(); } if (clearAfter) { clearPresenceQueue(); } - - if (isImpromptu_) { - setImpromptuWindowTitle(); - } } void MUCController::handleOccupantNicknameChanged(const std::string& oldNickname, const std::string& newNickname) { addPresenceMessage(generateNicknameChangeString(oldNickname, newNickname)); JID oldJID = muc_->getJID().withResource(oldNickname); JID newJID = muc_->getJID().withResource(newNickname); // adjust occupants currentOccupants_.erase(oldNickname); currentOccupants_.insert(newNickname); // adjust completer completer_->removeWord(oldNickname); completer_->addWord(newNickname); // update contact roster_->removeContact(oldJID); auto it = currentOccupants_.find(newNickname); if (it != currentOccupants_.end()) { roster_->removeContact(newJID); } MUCOccupant occupant = muc_->getOccupant(newNickname); JID realJID; if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } MUCOccupant::Role role = MUCOccupant::Participant; MUCOccupant::Affiliation affiliation = MUCOccupant::NoAffiliation; @@ -1151,75 +1143,60 @@ Form::ref MUCController::buildImpromptuRoomConfiguration(Form::ref roomConfigura } if (field->getName() == "muc#roomconfig_publicroom") { resultField = std::make_shared<FormField>(FormField::BooleanType, "0"); } if (field->getName() == "muc#roomconfig_whois") { resultField = std::make_shared<FormField>(FormField::ListSingleType, "anyone"); } if (field->getName() == "FORM_TYPE") { resultField = std::make_shared<FormField>(FormField::HiddenType, "http://jabber.org/protocol/muc#roomconfig"); } if (resultField) { impromptuConfigsMissing.erase(field->getName()); resultField->setName(field->getName()); result->addField(resultField); } } for (const auto& config : impromptuConfigsMissing) { if (config == "muc#roomconfig_publicroom") { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(QT_TRANSLATE_NOOP("", "This server doesn't support hiding your chat from other users.")), ChatWindow::DefaultDirection); } else if (config == "muc#roomconfig_whois") { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(QT_TRANSLATE_NOOP("", "This server doesn't support sharing people's real identity in this chat.")), ChatWindow::DefaultDirection); } } return result; } -void MUCController::setImpromptuWindowTitle() { - std::string title; - std::map<std::string, MUCOccupant> occupants = muc_->getOccupants(); - if (occupants.size() <= 1) { - title = QT_TRANSLATE_NOOP("", "Empty Chat"); - } else { - for (const auto& pair : occupants) { - if (pair.first != nick_) { - title += (title.empty() ? "" : ", ") + pair.first; - } - } - } - chatWindow_->setName(title); -} - void MUCController::handleRoomUnlocked() { // Handle buggy MUC implementations where the joined room already exists and is unlocked. // Configure the room again in this case. if (!isImpromptuAlreadyConfigured_) { if (isImpromptu_ && (muc_->getOccupant(nick_).getAffiliation() == MUCOccupant::Owner)) { muc_->requestConfigurationForm(); } else if (isImpromptu_) { onImpromptuConfigCompleted(); } } } void MUCController::setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) { ChatControllerBase::setAvailableServerFeatures(info); if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::BlockingCommandFeature)) { std::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&MUCController::handleBlockingStateChanged, this)); blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&MUCController::handleBlockingStateChanged, this)); blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&MUCController::handleBlockingStateChanged, this)); handleBlockingStateChanged(); } } void MUCController::handleMUCBookmarkAdded(const MUCBookmark& bookmark) { if (bookmark.getRoom() == muc_->getJID()) { updateChatWindowBookmarkStatus(bookmark); } } @@ -1234,31 +1211,36 @@ void MUCController::updateChatWindowBookmarkStatus(const boost::optional<MUCBook assert(chatWindow_); if (bookmark) { if (bookmark->getAutojoin()) { chatWindow_->setBookmarkState(ChatWindow::RoomAutoJoined); } else { chatWindow_->setBookmarkState(ChatWindow::RoomBookmarked); } } else { chatWindow_->setBookmarkState(ChatWindow::RoomNotBookmarked); } } void MUCController::displaySubjectIfChanged(const std::string& subject) { if (subject_ != subject) { if (!subject.empty()) { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % subject)), ChatWindow::DefaultDirection); } else { chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The room subject has been removed")))), ChatWindow::DefaultDirection); } subject_ = subject; } } void MUCController::addChatSystemMessage() { lastJoinMessageUID_ = chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(lastStartMessage_), ChatWindow::DefaultDirection); } +void MUCController::setChatWindowTitle(const std::string& title) { + chatWindowTitle_ = title; + chatWindow_->setName(chatWindowTitle_); +} + } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index b8f2aac..258eaaf 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -46,60 +46,61 @@ namespace Swift { enum JoinPart {Join, Part, JoinThenPart, PartThenJoin}; struct NickJoinPart { NickJoinPart(const std::string& nick, JoinPart type) : nick(nick), type(type) {} std::string nick; JoinPart type; }; class MUCController : public ChatControllerBase { public: MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* xmppRoster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, std::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager, MUCBookmarkManager* mucBookmarkManager); virtual ~MUCController(); boost::signals2::signal<void ()> onUserLeft; boost::signals2::signal<void ()> onUserJoined; boost::signals2::signal<void ()> onImpromptuConfigCompleted; boost::signals2::signal<void (const std::string&, const std::string& )> onUserNicknameChanged; virtual void setOnline(bool online) SWIFTEN_OVERRIDE; virtual void setAvailableServerFeatures(std::shared_ptr<DiscoInfo> info) SWIFTEN_OVERRIDE; void rejoin(); static void appendToJoinParts(std::vector<NickJoinPart>& joinParts, const NickJoinPart& newEvent); static std::string generateJoinPartString(const std::vector<NickJoinPart>& joinParts, bool isImpromptu); static std::string concatenateListOfNames(const std::vector<NickJoinPart>& joinParts); static std::string generateNicknameChangeString(const std::string& oldNickname, const std::string& newNickname); bool isJoined(); const std::string& getNick(); const boost::optional<std::string> getPassword() const; bool isImpromptu() const; std::map<std::string, JID> getParticipantJIDs() const; void sendInvites(const std::vector<JID>& jids, const std::string& reason) const; + void setChatWindowTitle(const std::string& title); protected: virtual void preSendMessageRequest(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE; virtual bool isIncomingMessageFromMe(std::shared_ptr<Message> message) SWIFTEN_OVERRIDE; virtual std::string senderHighlightNameFromMessage(const JID& from) SWIFTEN_OVERRIDE; virtual std::string senderDisplayNameFromMessage(const JID& from) SWIFTEN_OVERRIDE; virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(std::shared_ptr<Message> message) const SWIFTEN_OVERRIDE; virtual void preHandleIncomingMessage(std::shared_ptr<MessageEvent>) SWIFTEN_OVERRIDE; virtual void addMessageHandleIncomingMessage(const JID& from, const ChatWindow::ChatMessage& message, const std::string& messageID, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& time) SWIFTEN_OVERRIDE; virtual void handleIncomingReplaceMessage(const JID& from, const ChatWindow::ChatMessage& message, const std::string& messageID, const std::string& idToReplace, bool senderIsSelf, std::shared_ptr<SecurityLabel> label, const boost::posix_time::ptime& timeStamp) SWIFTEN_OVERRIDE; virtual void postHandleIncomingMessage(std::shared_ptr<MessageEvent>, const ChatWindow::ChatMessage& chatMessage) SWIFTEN_OVERRIDE; virtual void cancelReplaces() SWIFTEN_OVERRIDE; virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) SWIFTEN_OVERRIDE; virtual JID messageCorrectionJID(const JID& fromJID) SWIFTEN_OVERRIDE; private: void setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role); void clearPresenceQueue(); void addPresenceMessage(const std::string& message); void handleWindowOccupantSelectionChanged(ContactRosterItem* item); void handleActionRequestedOnOccupant(ChatWindow::OccupantAction, ContactRosterItem* item); void handleWindowClosed(); void handleAvatarChanged(const JID& jid); void handleOccupantJoined(const MUCOccupant& occupant); void handleOccupantNicknameChanged(const std::string& oldNickname, const std::string& newNickname); void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const std::string& reason); void handleOccupantPresenceChange(std::shared_ptr<Presence> presence); void handleOccupantRoleChanged(const std::string& nick, const MUCOccupant& occupant,const MUCOccupant::Role& oldRole); void handleOccupantAffiliationChanged(const std::string& nick, const MUCOccupant::Affiliation& affiliation,const MUCOccupant::Affiliation& oldAffiliation); void handleJoinComplete(const std::string& nick); @@ -107,86 +108,86 @@ namespace Swift { void handleJoinTimeoutTick(); void handleChangeSubjectRequest(const std::string&); void handleBookmarkRequest(); std::string roleToGroupName(MUCOccupant::Role role); std::string roleToSortName(MUCOccupant::Role role); JID nickToJID(const std::string& nick); std::string roleToFriendlyName(MUCOccupant::Role role); void receivedActivity(); bool messageTargetsMe(std::shared_ptr<Message> message); void updateJoinParts(); bool shouldUpdateJoinParts(); virtual void dayTicked() SWIFTEN_OVERRIDE { clearPresenceQueue(); } void processUserPart(); virtual void handleBareJIDCapsChanged(const JID& jid) SWIFTEN_OVERRIDE; void handleConfigureRequest(Form::ref); void handleConfigurationFailed(ErrorPayload::ref); void handleConfigurationFormReceived(Form::ref); void handleDestroyRoomRequest(); void handleInvitePersonToThisMUCRequest(const std::vector<JID>& jidsToInvite); void handleConfigurationCancelled(); void handleOccupantRoleChangeFailed(ErrorPayload::ref, const JID&, MUCOccupant::Role); void handleGetAffiliationsRequest(); void handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids); void handleChangeAffiliationsRequest(const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes); void handleInviteToMUCWindowDismissed(); void handleInviteToMUCWindowCompleted(); void handleUIEvent(std::shared_ptr<UIEvent> event); void addRecentLogs(); void checkDuplicates(std::shared_ptr<Message> newMessage); void setNick(const std::string& nick); - void setImpromptuWindowTitle(); void handleRoomUnlocked(); void configureAsImpromptuRoom(Form::ref form); Form::ref buildImpromptuRoomConfiguration(Form::ref roomConfigurationForm); void handleUnblockUserRequest(); void handleBlockingStateChanged(); void handleMUCBookmarkAdded(const MUCBookmark& bookmark); void handleMUCBookmarkRemoved(const MUCBookmark& bookmark); void updateChatWindowBookmarkStatus(const boost::optional<MUCBookmark>& bookmark); void displaySubjectIfChanged(const std::string& sucject); void addChatSystemMessage(); private: MUC::ref muc_; std::string nick_; std::string desiredNick_; TabComplete* completer_; bool parting_; bool joined_; bool shouldJoinOnReconnect_; bool doneGettingHistory_; boost::signals2::scoped_connection avatarChangedConnection_; std::shared_ptr<Timer> loginCheckTimer_; std::set<std::string> currentOccupants_; std::vector<NickJoinPart> joinParts_; boost::posix_time::ptime lastActivity_; boost::optional<std::string> password_; XMPPRoster* xmppRoster_; std::unique_ptr<Roster> roster_; std::vector<HistoryMessage> joinContext_; size_t renameCounter_; bool isImpromptu_; bool isImpromptuAlreadyConfigured_; RosterVCardProvider* rosterVCardProvider_; std::string lastJoinMessageUID_; std::string lastStartMessage_; ClientBlockListManager* clientBlockListManager_; boost::signals2::scoped_connection blockingOnStateChangedConnection_; boost::signals2::scoped_connection blockingOnItemAddedConnection_; boost::signals2::scoped_connection blockingOnItemRemovedConnection_; boost::optional<ChatWindow::AlertID> blockedContactAlert_; MUCBookmarkManager* mucBookmarkManager_; boost::signals2::scoped_connection mucBookmarkManagerBookmarkAddedConnection_; boost::signals2::scoped_connection mucBookmarkManagerBookmarkRemovedConnection_; std::string subject_; bool isInitialJoin_; + std::string chatWindowTitle_; }; } diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 52537a9..20c959c 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -125,60 +125,61 @@ class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST(testPresenceChangeDoesNotReplaceMUCInvite); CPPUNIT_TEST(testNotSplittingMUCPresenceJoinLeaveLinesOnChatStateNotifications); // MUC PM Tests CPPUNIT_TEST(testChatControllerPMPresenceHandling); CPPUNIT_TEST(testChatControllerMucPmUnavailableErrorHandling); // Highlighting tests CPPUNIT_TEST(testChatControllerHighlightingNotificationTesting); CPPUNIT_TEST(testChatControllerHighlightingNotificationDeduplicateSounds); CPPUNIT_TEST(testChatControllerHighlightingNotificationKeyword); CPPUNIT_TEST(testChatControllerMeMessageHandling); CPPUNIT_TEST(testRestartingMUCComponentCrash); CPPUNIT_TEST(testChatControllerMeMessageHandlingInMUC); // Carbons tests CPPUNIT_TEST(testCarbonsForwardedIncomingMessageToSecondResource); CPPUNIT_TEST(testCarbonsForwardedOutgoingMessageFromSecondResource); CPPUNIT_TEST(testCarbonsForwardedIncomingDuplicates); // Message correction tests CPPUNIT_TEST(testChatControllerMessageCorrectionCorrectReplaceID); CPPUNIT_TEST(testChatControllerMessageCorrectionIncorrectReplaceID); CPPUNIT_TEST(testChatControllerMessageCorrectionReplaceBySameResource); CPPUNIT_TEST(testChatControllerMessageCorrectionReplaceByOtherResource); CPPUNIT_TEST(testMUCControllerMessageCorrectionNoIDMatchRequired); //Imptomptu test CPPUNIT_TEST(testImpromptuChatTitle); + CPPUNIT_TEST(testImpromptuChatWindowTitle); CPPUNIT_TEST_SUITE_END(); public: void setUp() { mocks_ = new MockRepository(); notifier_ = std::unique_ptr<DummyNotifier>(new DummyNotifier()); jid_ = JID("test@test.com/resource"); stanzaChannel_ = new DummyStanzaChannel(); iqRouter_ = new IQRouter(stanzaChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>(); joinMUCWindowFactory_ = mocks_->InterfaceMock<JoinMUCWindowFactory>(); xmppRoster_ = new XMPPRosterImpl(); mucRegistry_ = new MUCRegistry(); nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, nullptr, mucRegistry_); presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_); serverDiscoInfo_ = std::make_shared<DiscoInfo>(); presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_); directedPresenceSender_ = new DirectedPresenceSender(presenceSender_); mucManager_ = new MUCManager(stanzaChannel_, iqRouter_, directedPresenceSender_, mucRegistry_); uiEventStream_ = new UIEventStream(); entityCapsProvider_ = new DummyEntityCapsProvider(); chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>(); mucSearchWindowFactory_ = mocks_->InterfaceMock<MUCSearchWindowFactory>(); settings_ = new DummySettingsProvider(); profileSettings_ = new ProfileSettingsProvider("a", settings_); chatListWindow_ = new MockChatListWindow(); ftManager_ = new DummyFileTransferManager(); ftOverview_ = new FileTransferOverview(ftManager_); @@ -1484,123 +1485,133 @@ public: userPayload->addItem(MUCItem(MUCOccupant::Member, JID("foo@bar.com"), MUCOccupant::Moderator)); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } { Message::ref mucMirrored = std::make_shared<Message>(); mucMirrored->setFrom(mucJID.withResource(nickname)); mucMirrored->setTo(jid_); mucMirrored->setType(Message::Groupchat); mucMirrored->setID("fooBlaID_1"); mucMirrored->setBody("Some misssssspelled message."); manager_->handleIncomingMessage(mucMirrored); } CPPUNIT_ASSERT_EQUAL(std::string("Some misssssspelled message."), window->bodyFromMessage(window->lastAddedMessage_)); // Replace message with non-matching ID { Message::ref mucMirrored = std::make_shared<Message>(); mucMirrored->setFrom(mucJID.withResource(nickname)); mucMirrored->setTo(jid_); mucMirrored->setType(Message::Groupchat); mucMirrored->setID("fooBlaID_3"); mucMirrored->setBody("Some correctly spelled message."); mucMirrored->addPayload(std::make_shared<Replace>("fooBlaID_2")); manager_->handleIncomingMessage(mucMirrored); } CPPUNIT_ASSERT_EQUAL(std::string("Some correctly spelled message."), window->bodyFromMessage(window->lastReplacedMessage_)); } - void testImpromptuChatTitle() { + void impromptuChatSetup(MockChatWindow* window) { stanzaChannel_->uniqueIDs_ = true; JID mucJID("795B7BBE-9099-4A0D-81BA-C816F78E275C@test.com"); manager_->setOnline(true); - // Open chat window to a sender. - MockChatWindow* window = new MockChatWindow(); std::shared_ptr<IQ> infoRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[1]); CPPUNIT_ASSERT(infoRequest); std::shared_ptr<IQ> infoResponse = IQ::createResult(infoRequest->getFrom(), infoRequest->getTo(), infoRequest->getID()); DiscoInfo info; info.addIdentity(DiscoInfo::Identity("Shakespearean Chat Service", "conference", "text")); info.addFeature("http://jabber.org/protocol/muc"); infoResponse->addPayload(std::make_shared<DiscoInfo>(info)); stanzaChannel_->onIQReceived(infoResponse); std::vector<JID> jids; jids.emplace_back("foo@test.com"); jids.emplace_back("bar@test.com"); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared<CreateImpromptuMUCUIEvent>(jids, mucJID, "")); CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); auto mucJoinPresence = std::dynamic_pointer_cast<Presence>(stanzaChannel_->sentStanzas[2]); CPPUNIT_ASSERT(mucJoinPresence); // MUC presence reply auto mucResponse = Presence::create(); mucResponse->setTo(jid_); mucResponse->setFrom(mucJoinPresence->getTo()); mucResponse->addPayload([]() { auto mucUser = std::make_shared<MUCUserPayload>(); mucUser->addItem(MUCItem(MUCOccupant::Member, MUCOccupant::Participant)); mucUser->addStatusCode(MUCUserPayload::StatusCode(110)); mucUser->addStatusCode(MUCUserPayload::StatusCode(210)); return mucUser; }()); stanzaChannel_->onPresenceReceived(mucResponse); // Before people join the impromptu room, the title is based on names coming from Roster CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); auto mucParticipantJoined = [&](const JID& jid) { auto participantJoinedPresence = Presence::create(); participantJoinedPresence->setTo(jid_); participantJoinedPresence->setFrom(mucJID.withResource(jid.toString())); auto mucUser = std::make_shared<MUCUserPayload>(); mucUser->addItem(MUCItem(MUCOccupant::Member, MUCOccupant::Participant)); participantJoinedPresence->addPayload(mucUser); return participantJoinedPresence; }; for (const auto& participantJID : jids) { stanzaChannel_->onPresenceReceived(mucParticipantJoined(participantJID)); } + } + void testImpromptuChatTitle() { + // Open a new chat window to a sender. + MockChatWindow* window = new MockChatWindow(); + impromptuChatSetup(window); // After people joined, the title is the list of participant nicknames or names coming from Roster (if nicknames are unavailable) CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); } + void testImpromptuChatWindowTitle() { + // Open a new chat window to a sender. + MockChatWindow* window = new MockChatWindow(); + impromptuChatSetup(window); + // After people joined, the title of chat window is combined of participant nicknames or names coming from Roster (if nicknames are unavailable) + CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), window->name_); + } private: std::shared_ptr<Message> makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) { std::shared_ptr<Message> message = std::make_shared<Message>(); message->setFrom(from); message->setID(id); message->setBody("This will cause the window to open"); message->addPayload(std::make_shared<DeliveryReceiptRequest>()); return message; } size_t st(int i) { return static_cast<size_t>(i); } void handleHighlightAction(const HighlightAction& action) { handledHighlightActions_++; if (action.getSoundFilePath()) { soundsPlayed_.insert(action.getSoundFilePath().get_value_or("")); } } private: JID jid_; std::unique_ptr<DummyNotifier> notifier_; ChatsManager* manager_; DummyStanzaChannel* stanzaChannel_; IQRouter* iqRouter_; EventController* eventController_; ChatWindowFactory* chatWindowFactory_; diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp index e7b4b3e..1f69f4f 100644 --- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp @@ -38,60 +38,63 @@ #include <Swift/Controllers/Roster/Roster.h> #include <Swift/Controllers/Settings/DummySettingsProvider.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h> #include <Swift/Controllers/UnitTest/MockChatWindow.h> #include <Swift/Controllers/XMPPEvents/EventController.h> using namespace Swift; class MUCControllerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(MUCControllerTest); CPPUNIT_TEST(testJoinPartStringContructionSimple); CPPUNIT_TEST(testJoinPartStringContructionMixed); CPPUNIT_TEST(testAppendToJoinParts); CPPUNIT_TEST(testAddressedToSelf); CPPUNIT_TEST(testNotAddressedToSelf); CPPUNIT_TEST(testAddressedToSelfBySelf); CPPUNIT_TEST(testMessageWithEmptyLabelItem); CPPUNIT_TEST(testMessageWithLabelItem); CPPUNIT_TEST(testCorrectMessageWithLabelItem); CPPUNIT_TEST(testRoleAffiliationStates); CPPUNIT_TEST(testSubjectChangeCorrect); CPPUNIT_TEST(testSubjectChangeIncorrectA); CPPUNIT_TEST(testSubjectChangeIncorrectB); CPPUNIT_TEST(testSubjectChangeIncorrectC); CPPUNIT_TEST(testHandleOccupantNicknameChanged); CPPUNIT_TEST(testHandleOccupantNicknameChangedRoster); CPPUNIT_TEST(testHandleChangeSubjectRequest); + + CPPUNIT_TEST(testNonImpromptuMUCWindowTitle); + CPPUNIT_TEST_SUITE_END(); public: void setUp() { crypto_ = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); self_ = JID("girl@wonderland.lit/rabbithole"); nick_ = "aLiCe"; mucJID_ = JID("teaparty@rooms.wonderland.lit"); mocks_ = new MockRepository(); stanzaChannel_ = new DummyStanzaChannel(); iqChannel_ = new DummyIQChannel(); iqRouter_ = new IQRouter(iqChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>(); userSearchWindowFactory_ = mocks_->InterfaceMock<UserSearchWindowFactory>(); xmppRoster_ = new XMPPRosterImpl(); presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_); presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_); directedPresenceSender_ = new DirectedPresenceSender(presenceSender_); uiEventStream_ = new UIEventStream(); avatarManager_ = new NullAvatarManager(); TimerFactory* timerFactory = nullptr; window_ = new MockChatWindow(); mucRegistry_ = new MUCRegistry(); entityCapsProvider_ = new DummyEntityCapsProvider(); settings_ = new DummySettingsProvider(); highlightManager_ = new HighlightManager(settings_); highlightManager_->resetToDefaultConfiguration(); muc_ = std::make_shared<MockMUC>(mucJID_); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_); @@ -559,60 +562,64 @@ public: CPPUNIT_ASSERT_EQUAL(0, occupantCount("TestUserOne")); CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserTwo")); CPPUNIT_ASSERT_EQUAL(1, occupantCount("TestUserThree")); } void testRoleAffiliationStatesVerify(const std::map<std::string, MUCOccupant> &occupants) { /* verify that the roster is in sync */ GroupRosterItem* group = window_->getRosterModel()->getRoot(); for (auto rosterItem : group->getChildren()) { GroupRosterItem* child = dynamic_cast<GroupRosterItem*>(rosterItem); CPPUNIT_ASSERT(child); for (auto childItem : child->getChildren()) { ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(childItem); CPPUNIT_ASSERT(item); std::map<std::string, MUCOccupant>::const_iterator occupant = occupants.find(item->getJID().getResource()); CPPUNIT_ASSERT(occupant != occupants.end()); CPPUNIT_ASSERT(item->getMUCRole() == occupant->second.getRole()); CPPUNIT_ASSERT(item->getMUCAffiliation() == occupant->second.getAffiliation()); } } } void testHandleChangeSubjectRequest() { std::string testStr("New Subject"); CPPUNIT_ASSERT_EQUAL(std::string(""), muc_->newSubjectSet_); window_->onChangeSubjectRequest(testStr); CPPUNIT_ASSERT_EQUAL(testStr, muc_->newSubjectSet_); } + void testNonImpromptuMUCWindowTitle() { + CPPUNIT_ASSERT_EQUAL(muc_->getJID().getNode(), window_->name_); + } + private: JID self_; JID mucJID_; MockMUC::ref muc_; std::string nick_; DummyStanzaChannel* stanzaChannel_; DummyIQChannel* iqChannel_; IQRouter* iqRouter_; EventController* eventController_; ChatWindowFactory* chatWindowFactory_; UserSearchWindowFactory* userSearchWindowFactory_; MUCController* controller_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; StanzaChannelPresenceSender* presenceSender_; DirectedPresenceSender* directedPresenceSender_; MockRepository* mocks_; UIEventStream* uiEventStream_; MockChatWindow* window_; MUCRegistry* mucRegistry_; DummyEntityCapsProvider* entityCapsProvider_; DummySettingsProvider* settings_; HighlightManager* highlightManager_; std::shared_ptr<ChatMessageParser> chatMessageParser_; std::shared_ptr<CryptoProvider> crypto_; VCardManager* vcardManager_; VCardMemoryStorage* vcardStorage_; ClientBlockListManager* clientBlockListManager_; MUCBookmarkManager* mucBookmarkManager_; |