summaryrefslogtreecommitdiffstats
path: root/Swift
diff options
context:
space:
mode:
Diffstat (limited to 'Swift')
-rw-r--r--Swift/ChangeLog.md4
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp12
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp14
3 files changed, 28 insertions, 2 deletions
diff --git a/Swift/ChangeLog.md b/Swift/ChangeLog.md
index 82db988..1b0eaca 100644
--- a/Swift/ChangeLog.md
+++ b/Swift/ChangeLog.md
@@ -1,30 +1,34 @@
+4.0-in-progress
+---------------
+- Fix regression in chat window titles for chat rooms
+
4.0-rc1 ( 2017-05-17 )
----------------------
- Fix UI layout issue for translations that require right-to-left (RTL) layout
- macOS releases are now code-signed with a key from Apple, so they can be run without Gatekeeper trust warnings
- Handle sessions being closed by the server
- Fix Last Message Correction in multi client scenarios
- Fix display of default avatar on Windows
- Support for automatic software updates on macOS
- Redesigned keyword highlighing
- Support for unicode emojis on macOS
- Improvements to font size handling in the chat theme
- Fix UX issues in trellis mode
- Improve date formatting
- And assorted smaller features and usability enhancements
4.0-beta2 ( 2016-07-20 )
------------------------
- Fix Swift bug introduced in 4.0-beta1 that results in the UI sometimes getting stuck during login
4.0-beta1 ( 2016-07-15 )
------------------------
- Support for message carbons (XEP-0280)
- Improved spell checker support on Linux
- Enabled trellis mode as a default feature, allowing several tiled chats windows to be shown at once
- New chat theme including a new font
- And assorted smaller features and usability enhancements
3.0 ( 2016-02-24 )
------------------
- File transfer and Mac Notification Center issues fixed
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 190ca8b..b7087fd 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -408,60 +408,69 @@ ChatListWindow::Chat ChatsManager::createChatListChatItem(const JID& jid, const
std::map<JID, std::string> participantsNames;
for (auto& i : invitees_[jid]) {
participantsNames.emplace(i, roster_->getNameForJID(i));
}
chat.inviteesNames = participantsNames;
return chat;
}
}
return ChatListWindow::Chat(jid, jid.toString(), activity, unreadCount, type, boost::filesystem::path(), true, privateMessage, nick, password);
} else {
ChatController* controller = getChatControllerIfExists(jid, false);
if (controller) {
unreadCount = controller->getUnreadCount();
}
JID bareishJID = mucRegistry_->isMUC(jid.toBare()) ? jid : jid.toBare();
Presence::ref presence = presenceOracle_->getAccountPresence(bareishJID);
StatusShow::Type type = presence ? presence->getShow() : StatusShow::None;
boost::filesystem::path avatarPath = avatarManager_ ? avatarManager_->getAvatarPath(bareishJID) : boost::filesystem::path();
return ChatListWindow::Chat(bareishJID, nickResolver_->jidToNick(bareishJID), activity, unreadCount, type, avatarPath, false, privateMessage);
}
}
void ChatsManager::handleChatActivity(const JID& jid, const std::string& activity, bool isMUC) {
const bool privateMessage = mucRegistry_->isMUC(jid.toBare()) && !isMUC;
ChatListWindow::Chat chat = createChatListChatItem(jid, activity, privateMessage);
/* FIXME: handle nick changes */
appendRecent(chat);
handleUnreadCountChanged(nullptr);
saveRecents();
+
+ // Look up potential MUC controller and update title accordingly as people
+ // join impromptu chats.
+ if (mucControllers_.find(jid) != mucControllers_.end()) {
+ auto chatListWindowIter = std::find_if(recentChats_.begin(), recentChats_.end(), [&](const ChatListWindow::Chat& chatListWindow) { return jid == (chatListWindow.jid); });
+ if (chatListWindowIter != recentChats_.end() && (mucControllers_[jid]->isImpromptu() || !chatListWindowIter->impromptuJIDs.empty())) {
+ mucControllers_[jid]->setChatWindowTitle(chatListWindowIter->getTitle());
+ }
+ }
}
void ChatsManager::handleChatClosed(const JID& /*jid*/) {
cleanupPrivateMessageRecents();
chatListWindow_->setRecents(recentChats_);
}
void ChatsManager::handleUnreadCountChanged(ChatControllerBase* controller) {
int unreadTotal = 0;
bool controllerIsMUC = dynamic_cast<MUCController*>(controller);
bool isPM = controller && !controllerIsMUC && mucRegistry_->isMUC(controller->getToJID().toBare());
for (ChatListWindow::Chat& chatItem : recentChats_) {
bool match = false;
if (controller) {
/* Matching MUC item */
match |= chatItem.isMUC == controllerIsMUC && chatItem.jid.toBare() == controller->getToJID().toBare();
/* Matching PM */
match |= isPM && chatItem.jid == controller->getToJID();
/* Matching non-PM */
match |= !isPM && !controllerIsMUC && chatItem.jid.toBare() == controller->getToJID().toBare();
}
if (match) {
chatItem.setUnreadCount(controller->getUnreadCount());
}
unreadTotal += chatItem.unreadCount;
}
chatListWindow_->setRecents(recentChats_);
chatListWindow_->setUnreadCount(unreadTotal);
}
@@ -848,62 +857,63 @@ 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()) {
+ if (chatListWindowIter != recentChats_.end() && (mucControllers_[mucJID]->isImpromptu() || !chatListWindowIter->impromptuJIDs.empty())) {
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));
}
}
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 20c959c..8fc26b5 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -123,63 +123,64 @@ class ChatsManagerTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testChatControllerFullJIDBindingOnTypingAndNotActive);
CPPUNIT_TEST(testLocalMUCServiceDiscoveryResetOnDisconnect);
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
+ // Chat window title tests
CPPUNIT_TEST(testImpromptuChatTitle);
CPPUNIT_TEST(testImpromptuChatWindowTitle);
+ CPPUNIT_TEST(testStandardMUCChatWindowTitle);
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_);
@@ -1559,60 +1560,71 @@ public:
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_);
}
+ void testStandardMUCChatWindowTitle() {
+ JID mucJID("mucroom@rooms.test.com");
+ std::string nickname = "toodles";
+
+ MockChatWindow* window = new MockChatWindow();
+ mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window);
+
+ uiEventStream_->send(std::make_shared<JoinMUCUIEvent>(mucJID, boost::optional<std::string>(), nickname));
+ CPPUNIT_ASSERT_EQUAL(std::string("mucroom"), 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_;
JoinMUCWindowFactory* joinMUCWindowFactory_;