From 2bb40b14d3d7f5cebc56601dad066d5201040bd2 Mon Sep 17 00:00:00 2001 From: Thanos Doukoudakis Date: Thu, 17 May 2018 17:36:44 +0100 Subject: Add a leave room option in MUC rooms This patch will add a leave room option in the cog menu of a MUC room, allowing the user to leave the room and close the window. Additionally when joining or leaving a room the autojoin field will change to true or false respectively. This patch also fixes a minor issue with a gui option that was not identifying bookmarked rooms at the cog menu. The autojoin option on the Enter room, Bookmark room and Edit Bookmark menu has been removed, since the default behaviour will be to automatically bookmark every MUC room the user joins, setting autojoin to true. If the user chooses to leave the room, then the autojoin flag will be set to false and the bookmark will be updated. Test-Information: Tested the changes in the UI in Windows Qt 5.9. Updated the ChatsManagerTests unit tests to check the chattables and the behaviour of the bookmarks, when joining and leaving MUCs. Change-Id: Iad1f34480a1e0b9df25c73b49247acc7b7825e20 diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 4d4ca86..8a26a56 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -378,7 +378,7 @@ void ChatControllerBase::handleMUCInvitation(Message::ref message) { MUCInvitationPayload::ref invite = message->getPayload(); if (autoAcceptMUCInviteDecider_->isAutoAcceptedInvite(message->getFrom(), invite)) { - eventStream_->send(std::make_shared(invite->getJID(), boost::optional(), boost::optional(), false, false, true)); + eventStream_->send(std::make_shared(invite->getJID(), boost::optional(), boost::optional(), false, true)); } else { MUCInviteEvent::ref inviteEvent = std::make_shared(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true, invite->getIsImpromptu()); handleGeneralMUCInvitation(inviteEvent); diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 63fd677..532b925 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -353,9 +353,11 @@ void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) { if (bookmark.getRoom().isBare() && !bookmark.getRoom().getNode().empty()) { std::map::iterator it = mucControllers_.find(bookmark.getRoom()); if (it == mucControllers_.end() && bookmark.getAutojoin()) { - handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false, false ); + handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false); } #ifndef NOT_YET + //Only one entry of the bookmark should exist + chatListWindow_->removeMUCBookmark(bookmark); chatListWindow_->addMUCBookmark(bookmark); #endif chattables_.addJID(bookmark.getRoom(), Chattables::State::Type::Room); @@ -534,6 +536,12 @@ void ChatsManager::handleUserLeftMUC(MUCController* mucController) { auto state = chattables_.getState(jid); state.status = StatusShow::None; chattables_.setState(jid, state); + //If user deletes bookmark from chatListWindow_ and then decides to leave the room, or if the server doesn't support bookmarks, the bookmark will not exist. + if (auto bookmarkFound = mucBookmarkManager_->lookupBookmark(jid)) { + MUCBookmark newBookmark(bookmarkFound.get()); + newBookmark.setAutojoin(false); + mucBookmarkManager_->replaceBookmark(bookmarkFound.get(), newBookmark); + } mucControllers_.erase(it); delete mucController; break; @@ -619,7 +627,7 @@ void ChatsManager::handleUIEvent(std::shared_ptr event) { invitees_[roomJID].insert(jid); } // join muc - MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional(), nickResolver_->jidToNick(jid_), false, true, true); + MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional(), nickResolver_->jidToNick(jid_), true, true); mucControllers_[roomJID]->onImpromptuConfigCompleted.connect(boost::bind(&ChatsManager::finalizeImpromptuJoin, this, muc, createImpromptuMUCEvent->getJIDs(), createImpromptuMUCEvent->getReason(), boost::optional())); mucControllers_[roomJID]->activateChatWindow(); } @@ -629,7 +637,7 @@ void ChatsManager::handleUIEvent(std::shared_ptr event) { mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark()); } else if (JoinMUCUIEvent::ref joinEvent = std::dynamic_pointer_cast(event)) { - handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getPassword(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically(), joinEvent->getCreateAsReservedRoomIfNew(), joinEvent->isImpromptu()); + handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getPassword(), joinEvent->getNick(), joinEvent->getCreateAsReservedRoomIfNew(), joinEvent->isImpromptu()); mucControllers_[joinEvent->getJID()]->activateChatWindow(); } else if (std::shared_ptr joinEvent = std::dynamic_pointer_cast(event)) { @@ -665,7 +673,7 @@ void ChatsManager::handleTransformChatToMUC(ChatController* chatController, Chat JID roomJID = JID(idGenerator_.generateID(), localMUCServiceJID_); // join muc - MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional(), nickResolver_->jidToNick(jid_), false, true, true, chatWindow); + MUC::ref muc = handleJoinMUCRequest(roomJID, boost::optional(), nickResolver_->jidToNick(jid_), true, true, chatWindow); mucControllers_[roomJID]->onImpromptuConfigCompleted.connect(boost::bind(&ChatsManager::finalizeImpromptuJoin, this, muc, jidsToInvite, reason, boost::optional(reuseChatInvite))); } @@ -839,20 +847,8 @@ void ChatsManager::rebindControllerJID(const JID& from, const JID& to) { chatControllers_[to]->setToJID(to); } -MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional& password, const boost::optional& nickMaybe, bool addAutoJoin, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow) { +MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional& password, const boost::optional& nickMaybe, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow) { MUC::ref muc; - if (addAutoJoin) { - MUCBookmark bookmark(mucJID, mucJID.getNode()); - bookmark.setAutojoin(true); - if (nickMaybe) { - bookmark.setNick(*nickMaybe); - } - if (password) { - bookmark.setPassword(*password); - } - mucBookmarkManager_->addBookmark(bookmark); - } - std::map::iterator it = mucControllers_.find(mucJID); if (it != mucControllers_.end()) { if (stanzaChannel_->isAvailable()) { @@ -875,6 +871,10 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti } std::shared_ptr chatMessageParser = std::make_shared(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_, settings_, chattables_); + chattables_.addJID(muc->getJID(), Chattables::State::Type::Room); + auto state = chattables_.getState(muc->getJID()); + state.status = StatusShow::Online; + chattables_.setState(muc->getJID(), state); 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 @@ -906,6 +906,24 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti mucControllers_[mucJID]->setChatWindowTitle(chatListWindowIter->getTitle()); } #endif + if (auto existingBookmark = mucBookmarkManager_->lookupBookmark(mucJID)) { + if (!existingBookmark->getAutojoin()) { + MUCBookmark newbookmark(existingBookmark.get()); + newbookmark.setAutojoin(true); + mucBookmarkManager_->replaceBookmark(*existingBookmark, newbookmark); + } + } + else { + MUCBookmark bookmark(mucJID, mucJID.getNode()); + bookmark.setAutojoin(true); + if (nickMaybe) { + bookmark.setNick(*nickMaybe); + } + if (password) { + bookmark.setPassword(*password); + } + mucBookmarkManager_->addBookmark(bookmark); + } mucControllers_[mucJID]->showChatWindow(); return muc; } @@ -1015,11 +1033,11 @@ void ChatsManager::handleIncomingMessage(std::shared_ptr incomingMessag ChatWindow* window = controller->detachChatWindow(); chatControllers_.erase(fromJID); delete controller; - handleJoinMUCRequest(invite->getJID(), boost::optional(), boost::optional(), false, false, true, window); + handleJoinMUCRequest(invite->getJID(), boost::optional(), boost::optional(), false, true, window); return; } } else { - handleJoinMUCRequest(invite->getJID(), boost::optional(), boost::optional(), false, false, true); + handleJoinMUCRequest(invite->getJID(), boost::optional(), boost::optional(), false, true); return; } } @@ -1091,7 +1109,7 @@ void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) { else if (chat.isMUC) { bool isImpromptu = (!chat.inviteesNames.empty() || !chat.impromptuJIDs.empty()); /* FIXME: This means that recents requiring passwords will just flat-out not work */ - uiEventStream_->send(std::make_shared(chat.jid, boost::optional(), chat.nick, false, false, isImpromptu)); + uiEventStream_->send(std::make_shared(chat.jid, boost::optional(), chat.nick, false, isImpromptu)); } else { uiEventStream_->send(std::make_shared(chat.jid)); @@ -1106,8 +1124,12 @@ void ChatsManager::handleChattableActivated(const JID& jid) { uiEventStream_->send(std::make_shared(jid)); } else if (state.type == Chattables::State::Type::Room) { - //FIXME: Find bookmarks and do handleMUCBookmarkActivated things - uiEventStream_->send(std::make_shared(jid, boost::optional(), boost::optional())); // Just a quick hack to reuse already open MUCs + if (auto foundBookmark = mucBookmarkManager_->lookupBookmark(jid)) { + handleMUCBookmarkActivated(foundBookmark.get()); + } + else { + uiEventStream_->send(std::make_shared(jid, boost::optional(), boost::optional())); // Just a quick hack to reuse already open MUCs + } } } diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index 0b85840..e120831 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -94,7 +94,7 @@ namespace Swift { #endif void handleChatRequest(const std::string& contact); void finalizeImpromptuJoin(MUC::ref muc, const std::vector& jidsToInvite, const std::string& reason, const boost::optional& reuseChatJID = boost::optional()); - MUC::ref handleJoinMUCRequest(const JID& muc, const boost::optional& password, const boost::optional& nick, bool addAutoJoin, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow = nullptr); + MUC::ref handleJoinMUCRequest(const JID& muc, const boost::optional& password, const boost::optional& nick, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow = nullptr); void handleSearchMUCRequest(); void handleMUCSelectedAfterSearch(const JID&); void rebindControllerJID(const JID& from, const JID& to); @@ -152,6 +152,9 @@ namespace Swift { ChatController* getChatControllerOrCreate(const JID &contact); ChatController* getChatControllerIfExists(const JID &contact, bool rebindIfNeeded = true); + protected: + MUCBookmarkManager* mucBookmarkManager_; + private: std::map mucControllers_; std::map chatControllers_; @@ -167,7 +170,6 @@ namespace Swift { AvatarManager* avatarManager_; PresenceSender* presenceSender_; UIEventStream* uiEventStream_; - MUCBookmarkManager* mucBookmarkManager_; std::shared_ptr serverDiscoInfo_; #ifndef NOT_YET ChatListWindow* chatListWindow_; diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 071b919..30d4933 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -60,17 +61,6 @@ namespace Swift { -class MUCBookmarkPredicate { - public: - MUCBookmarkPredicate(const JID& mucJID) : roomJID_(mucJID) { } - bool operator()(const MUCBookmark& operand) { - return operand.getRoom() == roomJID_; - } - - private: - JID roomJID_; -}; - /** * The controller does not gain ownership of the stanzaChannel, nor the factory. */ @@ -174,14 +164,7 @@ MUCController::MUCController ( mucBookmarkManagerBookmarkAddedConnection_ = (mucBookmarkManager_->onBookmarkAdded.connect(boost::bind(&MUCController::handleMUCBookmarkAdded, this, _1))); mucBookmarkManagerBookmarkRemovedConnection_ = (mucBookmarkManager_->onBookmarkRemoved.connect(boost::bind(&MUCController::handleMUCBookmarkRemoved, this, _1))); - std::vector mucBookmarks = mucBookmarkManager_->getBookmarks(); - std::vector::iterator bookmarkIterator = std::find_if(mucBookmarks.begin(), mucBookmarks.end(), MUCBookmarkPredicate(muc->getJID())); - if (bookmarkIterator != mucBookmarks.end()) { - updateChatWindowBookmarkStatus(*bookmarkIterator); - } - else { - updateChatWindowBookmarkStatus(boost::optional()); - } + updateChatWindowBookmarkStatus(mucBookmarkManager_->lookupBookmark(muc->getJID())); } MUCController::~MUCController() { @@ -498,6 +481,7 @@ void MUCController::setAvailableRoomActions(const MUCOccupant::Affiliation& affi if (role <= MUCOccupant::Visitor) { actions.push_back(ChatWindow::Invite); } + actions.push_back(ChatWindow::Leave); chatWindow_->setAvailableRoomActions(actions); } diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index e0a7fe3..7469d64 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +109,16 @@ class DummyNotifier : public Notifier { std::vector notifications; }; +class ExtendedChatsManager : public ChatsManager { +public: + ExtendedChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, const std::map& emoticons, VCardManager* vcardManager, Chattables& chattables) : + ChatsManager(jid, stanzaChannel, iqRouter, eventController, chatWindowFactory, joinMUCWindowFactory, nickResolver, presenceOracle, presenceSender, uiEventStream, chatListWindowFactory, useDelayForLatency, timerFactory, mucRegistry, entityCapsProvider, mucManager, mucSearchWindowFactory, profileSettings, ftOverview, roster, eagleMode, settings, historyController_, whiteboardManager, highlightManager, clientBlockListManager, emoticons, vcardManager, chattables) { + } + MUCBookmarkManager* getBookmarkManager() { + return mucBookmarkManager_; + } +}; + class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChatsManagerTest); @@ -162,6 +174,9 @@ class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST(testReceivingBookmarksWithDomainJID); CPPUNIT_TEST(testReceivingBookmarksWithBareJID); CPPUNIT_TEST(testReceivingBookmarksWithFullJID); + CPPUNIT_TEST(testAutoJoinBookmarksAndChattables); + CPPUNIT_TEST(testJoinNoAutojoinBookmark); + CPPUNIT_TEST(testJoinAndBookmarkMUC); CPPUNIT_TEST_SUITE_END(); @@ -209,7 +224,7 @@ public: clientBlockListManager_ = new ClientBlockListManager(iqRouter_); timerFactory_ = new DummyTimerFactory(); chattables_ = std::make_unique(); - manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, timerFactory_, mucRegistry_, entityCapsProvider_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, nullptr, wbManager_, highlightManager_, clientBlockListManager_, emoticons_, vcardManager_, *chattables_); + manager_ = new ExtendedChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, timerFactory_, mucRegistry_, entityCapsProvider_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, nullptr, wbManager_, highlightManager_, clientBlockListManager_, emoticons_, vcardManager_, *chattables_); manager_->setAvatarManager(avatarManager_); } @@ -1632,17 +1647,7 @@ public: CPPUNIT_ASSERT_EQUAL(std::string("mucroom"), window->name_); } - static std::shared_ptr createBookmarkStorageWithJID(const JID& jid) { - auto storage = std::make_shared(); - auto room = Storage::Room(); - room.jid = jid; - room.autoJoin = true; - storage->addRoom(room); - return storage; - } - - void testReceivingBookmarksWithDomainJID() { - auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + std::shared_ptr createBookmarkStorageWithJID(std::shared_ptr bookmarkRequest, const JID& jid, const bool autojoin) { CPPUNIT_ASSERT(bookmarkRequest); CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); @@ -1652,56 +1657,144 @@ public: auto storage = std::dynamic_pointer_cast(privateStorage->getPayload()); CPPUNIT_ASSERT(storage); + auto roomsStorage = std::make_shared(); + if (jid.isValid()) { + auto room = Storage::Room(); + room.jid = jid; + room.autoJoin = autojoin; + roomsStorage->addRoom(room); + } + return roomsStorage; + } + + void testReceivingBookmarksWithDomainJID() { + auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), - std::make_shared(createBookmarkStorageWithJID("montague.lit")) + std::make_shared(createBookmarkStorageWithJID(bookmarkRequest, "montague.lit", true)) ); stanzaChannel_->onIQReceived(response); } void testReceivingBookmarksWithBareJID() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); - CPPUNIT_ASSERT(bookmarkRequest); - CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + MockChatWindow* window = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(window); + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared(createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit", true)) + ); + stanzaChannel_->onIQReceived(response); + } - auto privateStorage = bookmarkRequest->getPayload(); - CPPUNIT_ASSERT(privateStorage); + void testReceivingBookmarksWithFullJID() { + auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared(createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit/someresource", true)) + ); + stanzaChannel_->onIQReceived(response); + } - auto storage = std::dynamic_pointer_cast(privateStorage->getPayload()); - CPPUNIT_ASSERT(storage); + void testAutoJoinBookmarksAndChattables() { - MockChatWindow* window = new MockChatWindow(); - mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(window); + auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "autojoin@bookmark.lit", true); + auto room = Storage::Room(); + room.jid = "noAutojoin@bookmark.lit"; + roomsStorage->addRoom(room); + + //Only autojoin@bookmark.lit window should open. + MockChatWindow* autojoinBookmarkWindow = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("autojoin@bookmark.lit"), uiEventStream_).Return(autojoinBookmarkWindow); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), - std::make_shared(createBookmarkStorageWithJID("example@montague.lit")) + std::make_shared(roomsStorage) ); stanzaChannel_->onIQReceived(response); + //Both bookmarks should be added to the chattables. + CPPUNIT_ASSERT_EQUAL(size_t(2), chattables_->get().size()); + auto autoJoinState = chattables_->getState("autojoin@bookmark.lit"); + CPPUNIT_ASSERT(autoJoinState.type == Chattables::State::Type::Room); + CPPUNIT_ASSERT_EQUAL(autoJoinState.status, StatusShow::Online); + auto noAutoJoinState = chattables_->getState("noAutojoin@bookmark.lit"); + CPPUNIT_ASSERT(noAutoJoinState.type == Chattables::State::Type::Room); + CPPUNIT_ASSERT_EQUAL(noAutoJoinState.status, StatusShow::None); } - void testReceivingBookmarksWithFullJID() { + void testJoinNoAutojoinBookmark() { + auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); - CPPUNIT_ASSERT(bookmarkRequest); - CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); + auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit", false); - auto privateStorage = bookmarkRequest->getPayload(); - CPPUNIT_ASSERT(privateStorage); + auto response = IQ::createResult( + bookmarkRequest->getFrom(), + bookmarkRequest->getTo(), + bookmarkRequest->getID(), + std::make_shared(roomsStorage) + ); + stanzaChannel_->onIQReceived(response); - auto storage = std::dynamic_pointer_cast(privateStorage->getPayload()); - CPPUNIT_ASSERT(storage); + //Join previous bookmarked room, expecting no increase in chattables and change of autojoin in bookmark to true + MockChatWindow* newExampleChatWindow = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(newExampleChatWindow); + uiEventStream_->send(std::make_shared("example@montague.lit", boost::optional(), boost::optional())); + CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size()); + auto state = chattables_->getState("example@montague.lit"); + CPPUNIT_ASSERT(state.type == Chattables::State::Type::Room); + CPPUNIT_ASSERT_EQUAL(state.status, StatusShow::Online); + + auto bookmarks = manager_->getBookmarkManager()->getBookmarks(); + CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1)); + CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit")); + CPPUNIT_ASSERT(bookmarks[0].getAutojoin()); + } + void testJoinAndBookmarkMUC() { + auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); + auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "", true); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), - std::make_shared(createBookmarkStorageWithJID("example@montague.lit/someresource")) + std::make_shared(roomsStorage) ); stanzaChannel_->onIQReceived(response); + + //Join non-bookmarked room expecting for the room to get bookmarked with autojoin to true + MockChatWindow* exampleChatWindow = new MockChatWindow(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With("example@montague.lit", uiEventStream_).Return(exampleChatWindow); + uiEventStream_->send(std::make_shared("example@montague.lit", boost::optional(), boost::optional())); + { + CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size()); + auto state = chattables_->getState("example@montague.lit"); + CPPUNIT_ASSERT(state.type == Chattables::State::Type::Room); + CPPUNIT_ASSERT_EQUAL(state.status, StatusShow::Online); + + auto bookmarks = manager_->getBookmarkManager()->getBookmarks(); + CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1)); + CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit")); + CPPUNIT_ASSERT(bookmarks[0].getAutojoin()); + + } + //Exiting room that is bookmarked, expecting chattable to stay but bookmark autojoin change to false. + exampleChatWindow->onClosed(); + { + CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size()); + auto bookmarks = manager_->getBookmarkManager()->getBookmarks(); + CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1)); + CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit")); + CPPUNIT_ASSERT(!bookmarks[0].getAutojoin()); + } } private: @@ -1728,7 +1821,7 @@ private: private: JID jid_; std::unique_ptr notifier_; - ChatsManager* manager_; + ExtendedChatsManager* manager_; DummyStanzaChannel* stanzaChannel_; IQRouter* iqRouter_; EventController* eventController_; diff --git a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h index 5d6df55..78103a8 100644 --- a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h +++ b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h @@ -19,7 +19,7 @@ namespace Swift { class JoinMUCUIEvent : public UIEvent { public: typedef std::shared_ptr ref; - JoinMUCUIEvent(const JID& jid, const boost::optional& password = boost::optional(), const boost::optional& nick = boost::optional(), bool joinAutomaticallyInFuture = false, bool createAsReservedRoomIfNew = false, bool isImpromptu = false, bool isContinuation = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture), createAsReservedRoomIfNew_(createAsReservedRoomIfNew), password_(password), isImpromptuMUC_(isImpromptu), isContinuation_(isContinuation) {} + JoinMUCUIEvent(const JID& jid, const boost::optional& password = boost::optional(), const boost::optional& nick = boost::optional(), bool createAsReservedRoomIfNew = false, bool isImpromptu = false, bool isContinuation = false) : jid_(jid), nick_(nick), createAsReservedRoomIfNew_(createAsReservedRoomIfNew), password_(password), isImpromptuMUC_(isImpromptu), isContinuation_(isContinuation) {} const boost::optional& getNick() const {return nick_;} const JID& getJID() const {return jid_;} bool getShouldJoinAutomatically() const {return joinAutomatically_;} diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index 507269f..daece0e 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -141,7 +141,7 @@ namespace Swift { enum AckState {Pending, Received, Failed}; enum ReceiptState {ReceiptRequested, ReceiptReceived, ReceiptFailed}; enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact, ShowProfile}; - enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite}; + enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite, Leave}; enum FileTransferState { Initialisation, ///< Collecting information required for sending the request out. WaitingForAccept, ///< The file transfer request was send out. diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp index 3caed57..e92ba0d 100644 --- a/Swift/QtUI/ChatList/QtChatListWindow.cpp +++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp @@ -86,11 +86,9 @@ void QtChatListWindow::handleClicked(const QModelIndex& index) { void QtChatListWindow::setupContextMenus() { mucMenu_ = new QMenu(); - onlineOnlyActions_ << mucMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark())); onlineOnlyActions_ << mucMenu_->addAction(tr("Edit Bookmark"), this, SLOT(handleEditBookmark())); onlineOnlyActions_ << mucMenu_->addAction(tr("Remove Bookmark"), this, SLOT(handleRemoveBookmark())); emptyMenu_ = new QMenu(); - onlineOnlyActions_ << emptyMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark())); } void QtChatListWindow::handleItemActivated(const QModelIndex& index) { @@ -146,22 +144,6 @@ void QtChatListWindow::handleRemoveBookmark() { eventStream_->send(std::make_shared(mucItem->getBookmark())); } -void QtChatListWindow::handleAddBookmarkFromRecents() { - const ChatListRecentItem* item = dynamic_cast(contextMenuItem_); - if (item) { - const ChatListWindow::Chat& chat = item->getChat(); - MUCBookmark bookmark(chat.jid, chat.jid.toBare().toString()); - bookmark.setNick(chat.nick); - bookmark.setPassword(chat.password); - eventStream_->send(std::make_shared(bookmark)); - } -} - -void QtChatListWindow::handleAddBookmark() { - (new QtAddBookmarkWindow(eventStream_))->show(); -} - - void QtChatListWindow::handleEditBookmark() { const ChatListMUCItem* mucItem = dynamic_cast(contextMenuItem_); if (!mucItem) return; @@ -208,11 +190,8 @@ void QtChatListWindow::contextMenuEvent(QContextMenuEvent* event) { if (mucItem) { contextMenuItem_ = mucItem; bookmarkAction = mucRecentsMenu.addAction(tr("Edit Bookmark"), this, SLOT(handleEditBookmark())); + bookmarkAction->setEnabled(isOnline_); } - else { - bookmarkAction = mucRecentsMenu.addAction(tr("Add to Bookmarks"), this, SLOT(handleAddBookmarkFromRecents())); - } - bookmarkAction->setEnabled(isOnline_); mucRecentsMenu.addAction(tr("Clear recents"), this, SLOT(handleClearRecentsRequested())); mucRecentsMenu.exec(QCursor::pos()); return; diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h index 834e318..001650c 100644 --- a/Swift/QtUI/ChatList/QtChatListWindow.h +++ b/Swift/QtUI/ChatList/QtChatListWindow.h @@ -35,10 +35,8 @@ namespace Swift { void onCountUpdated(int count); private slots: void handleItemActivated(const QModelIndex&); - void handleAddBookmark(); void handleEditBookmark(); void handleRemoveBookmark(); - void handleAddBookmarkFromRecents(); void handleClicked(const QModelIndex& index); void handleSettingChanged(const std::string& setting); void handleClearRecentsRequested(); diff --git a/Swift/QtUI/QtBookmarkDetailWindow.cpp b/Swift/QtUI/QtBookmarkDetailWindow.cpp index 920e94e..efa0e25 100644 --- a/Swift/QtUI/QtBookmarkDetailWindow.cpp +++ b/Swift/QtUI/QtBookmarkDetailWindow.cpp @@ -42,7 +42,7 @@ boost::optional QtBookmarkDetailWindow::createBookmarkFromForm() { MUCBookmark bookmark(room, name); std::string nick(Q2PSTRING(nick_->text())); std::string password(Q2PSTRING(password_->text())); - bookmark.setAutojoin(autojoin_->isChecked()); + bookmark.setAutojoin(true); if (!nick.empty()) { bookmark.setNick(nick); } @@ -68,12 +68,6 @@ void QtBookmarkDetailWindow::createFormFromBookmark(const MUCBookmark& bookmark) if (bookmark.getPassword()) { password_->setText(P2QSTRING((*bookmark.getPassword()))); } - - if (bookmark.getAutojoin()) { - autojoin_->setCheckState(Qt::Checked); - } else { - autojoin_->setCheckState(Qt::Unchecked); - } } } diff --git a/Swift/QtUI/QtBookmarkDetailWindow.ui b/Swift/QtUI/QtBookmarkDetailWindow.ui index be55686..affb7e4 100644 --- a/Swift/QtUI/QtBookmarkDetailWindow.ui +++ b/Swift/QtUI/QtBookmarkDetailWindow.ui @@ -82,29 +82,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Enter automatically - - - true - - - diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 7c0a9d8..a413b4d 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -751,6 +751,7 @@ void QtChatWindow::handleActionButtonClicked() { QAction* affiliations = nullptr; QAction* destroy = nullptr; QAction* invite = nullptr; + QAction* leave = nullptr; QAction* block = nullptr; QAction* unblock = nullptr; @@ -804,6 +805,10 @@ void QtChatWindow::handleActionButtonClicked() { invite = contextMenu.addAction(tr("Invite person to this room…")); invite->setEnabled(isOnline_); break; + case ChatWindow::Leave: + leave = contextMenu.addAction(tr("Leave room")); + leave->setEnabled(isOnline_); + break; } } } @@ -855,6 +860,9 @@ void QtChatWindow::handleActionButtonClicked() { else if (result == invite) { onInviteToChat(std::vector()); } + else if (result == leave) { + close(); + } else if (result == block) { onBlockUserRequest(); } @@ -888,14 +896,16 @@ void QtChatWindow::setCanInitiateImpromptuChats(bool supportsImpromptu) { } void QtChatWindow::showBookmarkWindow(const MUCBookmark& bookmark) { - if (roomBookmarkState_ == RoomNotBookmarked) { - QtAddBookmarkWindow* window = new QtAddBookmarkWindow(eventStream_, bookmark); + if (roomBookmarkState_ != RoomNotBookmarked) { + QtEditBookmarkWindow* window = new QtEditBookmarkWindow(eventStream_, bookmark); window->show(); } +#ifndef NOT_YET else { - QtEditBookmarkWindow* window = new QtEditBookmarkWindow(eventStream_, bookmark); + QtAddBookmarkWindow* window = new QtAddBookmarkWindow(eventStream_, bookmark); window->show(); } +#endif // ! NOT_YET } std::string QtChatWindow::getID() const { diff --git a/Swift/QtUI/QtEditBookmarkWindow.cpp b/Swift/QtUI/QtEditBookmarkWindow.cpp index 1d6b467..c724c97 100644 --- a/Swift/QtUI/QtEditBookmarkWindow.cpp +++ b/Swift/QtUI/QtEditBookmarkWindow.cpp @@ -12,7 +12,6 @@ namespace Swift { QtEditBookmarkWindow::QtEditBookmarkWindow(UIEventStream* eventStream, const MUCBookmark& bookmark) : eventStream_(eventStream), bookmark_(bookmark) { name_->setText(P2QSTRING(bookmark.getName())); room_->setText(P2QSTRING(bookmark.getRoom().toString())); - autojoin_->setChecked(bookmark.getAutojoin()); nick_->setText(bookmark.getNick() ? P2QSTRING(bookmark.getNick().get()) : ""); password_->setText(bookmark.getPassword() ? P2QSTRING(bookmark.getPassword().get()) : ""); } diff --git a/Swift/QtUI/QtJoinMUCWindow.cpp b/Swift/QtUI/QtJoinMUCWindow.cpp index 13de1c9..550ae4a 100644 --- a/Swift/QtUI/QtJoinMUCWindow.cpp +++ b/Swift/QtUI/QtJoinMUCWindow.cpp @@ -49,7 +49,7 @@ void QtJoinMUCWindow::handleJoin() { lastSetNick = Q2PSTRING(ui.nickName->text()); std::string password = Q2PSTRING(ui.password->text()); JID room(Q2PSTRING(ui.room->text())); - uiEventStream->send(std::make_shared(room, password, lastSetNick, ui.joinAutomatically->isChecked(), !ui.instantRoom->isChecked())); + uiEventStream->send(std::make_shared(room, password, lastSetNick, !ui.instantRoom->isChecked())); hide(); } diff --git a/Swift/QtUI/QtJoinMUCWindow.ui b/Swift/QtUI/QtJoinMUCWindow.ui index 24d6ab8..96f1d17 100644 --- a/Swift/QtUI/QtJoinMUCWindow.ui +++ b/Swift/QtUI/QtJoinMUCWindow.ui @@ -101,13 +101,6 @@ - - - Enter automatically in future - - - - Enter Room @@ -124,7 +117,6 @@ nickName password instantRoom - joinAutomatically joinButton diff --git a/Swift/QtUI/QtPlainChatView.cpp b/Swift/QtUI/QtPlainChatView.cpp index 5d23923..3d67091 100644 --- a/Swift/QtUI/QtPlainChatView.cpp +++ b/Swift/QtUI/QtPlainChatView.cpp @@ -327,7 +327,7 @@ void QtPlainChatView::acceptMUCInvite() { AcceptMUCInviteAction *action = dynamic_cast(sender()); if (action) { - eventStream_->send(std::make_shared(action->jid_.toString(), action->password_, boost::optional(), false, false, action->isImpromptu_, action->isContinuation_)); + eventStream_->send(std::make_shared(action->jid_.toString(), action->password_, boost::optional(), false, action->isImpromptu_, action->isContinuation_)); delete action->parent_; } } diff --git a/Swift/QtUI/QtWebKitChatView.cpp b/Swift/QtUI/QtWebKitChatView.cpp index 8ced8fc..f2effa8 100644 --- a/Swift/QtUI/QtWebKitChatView.cpp +++ b/Swift/QtUI/QtWebKitChatView.cpp @@ -811,7 +811,7 @@ void QtWebKitChatView::handleHTMLButtonClicked(QString id, QString encodedArgume QString elementID = arg3; QString isImpromptu = arg4; QString isContinuation = arg5; - eventStream_->send(std::make_shared(Q2PSTRING(roomJID), Q2PSTRING(password), boost::optional(), false, false, isImpromptu.contains("true"), isContinuation.contains("true"))); + eventStream_->send(std::make_shared(Q2PSTRING(roomJID), Q2PSTRING(password), boost::optional(), false, isImpromptu.contains("true"), isContinuation.contains("true"))); setMUCInvitationJoined(elementID); } else if (id.startsWith(ButtonResendPopup)) { diff --git a/Swiften/MUC/MUCBookmarkManager.cpp b/Swiften/MUC/MUCBookmarkManager.cpp index 9f8ae77..e394217 100644 --- a/Swiften/MUC/MUCBookmarkManager.cpp +++ b/Swiften/MUC/MUCBookmarkManager.cpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -30,6 +31,7 @@ void MUCBookmarkManager::handleBookmarksReceived(std::shared_ptr payloa } ready_ = true; + handlingReceivedBookmarks_ = true; onBookmarksReady(); storage = payload; @@ -47,18 +49,25 @@ void MUCBookmarkManager::handleBookmarksReceived(std::shared_ptr payloa onBookmarkRemoved(oldBookmark); } } - + std::vector newAddedBookmarksToBeSignaled; for (const auto& newBookmark : receivedBookmarks) { if (!containsEquivalent(bookmarks_, newBookmark)) { newBookmarks.push_back(newBookmark); - onBookmarkAdded(newBookmark); + //If the bookmark does not exist in bookmark manager, after emmiting the signal, chatsmanager will try to join the room, if the bookmark has autojoin to true. + //The bookmark is not yet available in bookmark manager, therefore a new bookmark will be created which will be lost when newBookmarks replace bookmarks. + newAddedBookmarksToBeSignaled.push_back(newBookmark); } } bookmarks_ = newBookmarks; + for (auto bookmark : newAddedBookmarksToBeSignaled) { + onBookmarkAdded(bookmark); + } + + handlingReceivedBookmarks_ = false; } bool MUCBookmarkManager::containsEquivalent(const std::vector& list, const MUCBookmark& bookmark) { - return std::find(list.begin(), list.end(), bookmark) != list.end(); + return std::find_if(list.begin(), list.end(), [&](const MUCBookmark& val) { return bookmark.getRoom() == val.getRoom(); }) != list.end(); } void MUCBookmarkManager::replaceBookmark(const MUCBookmark& oldBookmark, const MUCBookmark& newBookmark) { @@ -76,8 +85,15 @@ void MUCBookmarkManager::replaceBookmark(const MUCBookmark& oldBookmark, const M void MUCBookmarkManager::addBookmark(const MUCBookmark& bookmark) { if (!ready_) return; - bookmarks_.push_back(bookmark); - onBookmarkAdded(bookmark); + if (auto found = lookupBookmark(bookmark.getRoom())) { + if (found != bookmark) { + replaceBookmark(found.get(), bookmark); + } + } + else { + bookmarks_.push_back(bookmark); + onBookmarkAdded(bookmark); + } flush(); } @@ -96,6 +112,9 @@ void MUCBookmarkManager::removeBookmark(const MUCBookmark& bookmark) { } void MUCBookmarkManager::flush() { + if (handlingReceivedBookmarks_) { + return; + } if (!storage) { storage = std::make_shared(); } @@ -116,4 +135,12 @@ const std::vector& MUCBookmarkManager::getBookmarks() const { return bookmarks_; } +boost::optional MUCBookmarkManager::lookupBookmark(const JID& bookmarkJID) const { + auto bookmarkIterator = std::find_if(bookmarks_.begin(), bookmarks_.end(), [&](const MUCBookmark& val) { return bookmarkJID == val.getRoom(); }); + if (bookmarkIterator != bookmarks_.end()) { + return *bookmarkIterator; + } + return boost::none; +} + } diff --git a/Swiften/MUC/MUCBookmarkManager.h b/Swiften/MUC/MUCBookmarkManager.h index 78fbbb0..7f262da 100644 --- a/Swiften/MUC/MUCBookmarkManager.h +++ b/Swiften/MUC/MUCBookmarkManager.h @@ -27,8 +27,8 @@ namespace Swift { void addBookmark(const MUCBookmark& bookmark); void removeBookmark(const MUCBookmark& bookmark); void replaceBookmark(const MUCBookmark& oldBookmark, const MUCBookmark& newBookmark); - const std::vector& getBookmarks() const; + boost::optional lookupBookmark(const JID& bookmarkJID) const; public: boost::signals2::signal onBookmarkAdded; @@ -45,6 +45,7 @@ namespace Swift { private: bool ready_; + bool handlingReceivedBookmarks_; std::vector bookmarks_; IQRouter* iqRouter_; std::shared_ptr storage; -- cgit v0.10.2-6-g49f6