diff --git a/engine/ClientLogic.cpp b/engine/ClientLogic.cpp index 4f24b861..ad1f4f8e 100644 --- a/engine/ClientLogic.cpp +++ b/engine/ClientLogic.cpp @@ -318,6 +318,7 @@ static const String s_inviteContacts = "invite_contacts"; // List of contacts // Actions static const String s_actionShowCallsList = "showCallsList"; static const String s_actionShowNotification = "showNotification"; +static const String s_actionShowInfo = "showNotificationInfo"; static const String s_actionPendingChat = "showPendingChat"; static const String s_actionCall = "call"; static const String s_actionAnswer = "answer"; @@ -521,6 +522,22 @@ static inline bool setChangedParam(NamedList& dest, const String& param, return true; } +// Append failure reason/error to a string +static void addError(String& buf, NamedList& list) +{ + String* error = list.getParam("error"); + String* reason = list.getParam("reason"); + if (TelEngine::null(error)) { + if (TelEngine::null(reason)) + return; + error = reason; + reason = 0; + } + buf.append(*error,": "); + if (!TelEngine::null(reason)) + buf << " (" << *reason << ")"; +} + // Build contact name: name static inline void buildContactName(String& buf, ClientContact& c) { @@ -2525,6 +2542,8 @@ static bool dropFileTransferItem(const String& id) // Add a tray icon to the mainwindow stack static bool addTrayIcon(const String& type) { + if (!type) + return false; int prio = 0; String triggerAction; NamedList* iconParams = 0; @@ -2546,12 +2565,19 @@ static bool addTrayIcon(const String& type) triggerAction = s_actionShowCallsList; specific = "View calls"; } - else if (type == "notification") { - prio = Client::TrayIconNotification; + else if (type == "notification" || type == "info") { iconParams = new NamedList(name); - iconParams->addParam("icon",Client::s_skinPath + "tray_notification.png"); + if (type == "notification") { + prio = Client::TrayIconNotification; + iconParams->addParam("icon",Client::s_skinPath + "tray_notification.png"); + triggerAction = s_actionShowNotification; + } + else { + prio = Client::TrayIconInfo; + iconParams->addParam("icon",Client::s_skinPath + "tray_info.png"); + triggerAction = s_actionShowInfo; + } info << "\r\nA notification is requiring your attention"; - triggerAction = s_actionShowNotification; specific = "View notifications"; } else if (type == "incomingchat") { @@ -2583,7 +2609,8 @@ static bool addTrayIcon(const String& type) // Remove a tray icon from mainwindow stack static inline bool removeTrayIcon(const String& type) { - return Client::removeTrayIcon("mainwindow","mainwindow_" + type + "_icon"); + return type && + Client::removeTrayIcon("mainwindow","mainwindow_" + type + "_icon"); } // Notify incoming chat to the user @@ -4469,16 +4496,17 @@ bool DefaultLogic::action(Window* wnd, const String& name, NamedList* params) if (name == "button_hide" && wnd) return Client::self() && Client::self()->setVisible(wnd->toString(),false); // Show/hide messages - bool showMsgs = (name == "messages_show" || name == s_actionShowNotification); + bool showMsgs = (name == "messages_show" || name == s_actionShowNotification || + name == s_actionShowInfo); if (showMsgs || name == "messages_close") { - if (name == s_actionShowNotification) { - removeTrayIcon("notification"); + bool notif = (name == s_actionShowNotification); + if (notif || name == s_actionShowInfo) { + removeTrayIcon(notif ? "notification" : "info"); if (wnd && Client::valid()) Client::self()->setVisible(wnd->id(),true,true); } return showNotificationArea(showMsgs,wnd); } - // Dialog actions // Return 'true' to close the dialog bool dlgRet = false; @@ -4920,8 +4948,10 @@ bool DefaultLogic::select(Window* wnd, const String& name, const String& item, // No more notifications: remove the tray icon if (name == "messages") { - if (!item) + if (!item) { removeTrayIcon("notification"); + removeTrayIcon("info"); + } return true; } @@ -5925,47 +5955,27 @@ bool DefaultLogic::handleUserRoster(Message& msg, bool& stopLogic) const String& oper = msg["operation"]; if (!oper) return false; - bool fail = false; - bool remove = (oper != "update"); - if (remove && oper != "delete") { - if (oper != "queryerror") - return false; - fail = true; - } // Postpone message processing if (Client::self()->postpone(msg,Client::UserRoster)) { stopLogic = true; return false; } - int n = msg.getIntValue("contact.count"); - if (n < 1 && !fail) - return false; const String& account = msg["account"]; ClientAccount* a = account ? m_accounts->findAccount(account) : 0; if (!a) return false; - if (fail) { - String reason = msg["error"]; - if (reason) { - const String& res = msg["reason"]; - if (res) - reason << " (" << res << ")"; - } - else - reason = msg["reason"]; - NamedList list(""); - NamedList* upd = buildNotifArea(list,"rosterreqfail",account, - String::empty(),"Friends list failure"); - setGenericNotif(*upd,"Retry"); - String text; - text << "Failed to retrieve the friends list"; - text.append(reason,": "); - text.append(account,"\r\nAccount: "); - upd->addParam("text",text); - showNotificationArea(true,Client::self()->getWindow(s_wndMain),&list); + if (oper == "error" || oper == "queryerror" || oper == "result") { + showUserRosterNotification(a,oper,msg,msg["contact"]); return false; } - if (msg.getBoolValue("queryrsp")) + bool remove = (oper != "update"); + if (remove && oper != "delete") + return false; + int n = msg.getIntValue("contact.count"); + if (n < 1) + return false; + bool queryRsp = msg.getBoolValue("queryrsp"); + if (queryRsp) removeNotifArea("rosterreqfail",account); ObjList removed; NamedList chatlist(""); @@ -5983,6 +5993,8 @@ bool DefaultLogic::handleUserRoster(Message& msg, bool& stopLogic) if (remove) { if (!c) continue; + if (!queryRsp) + showUserRosterNotification(a,oper,msg,uri); removed.append(a->removeContact(id,false)); continue; } @@ -6002,9 +6014,13 @@ bool DefaultLogic::handleUserRoster(Message& msg, bool& stopLogic) changed = setChangedString(c->m_subscription,sub) || changed; // Get groups changed = c->setGroups(msg,pref + "group") || changed; - // Update info window if displayed - if (changed) + if (changed) { + // Update info window if displayed updateContactInfo(c); + // Show update notification + if (!queryRsp) + showUserRosterNotification(a,oper,msg,uri,newContact); + } if (!(changed && a->hasChat())) continue; NamedList* p = new NamedList(c->toString()); @@ -8646,16 +8662,17 @@ bool DefaultLogic::handleMucResNotify(Message& msg, ClientAccount* acc, const St } // Show/hide the notification area (messages) -bool DefaultLogic::showNotificationArea(bool show, Window* wnd, NamedList* upd) +bool DefaultLogic::showNotificationArea(bool show, Window* wnd, NamedList* upd, + const char* notif) { if (!Client::self()) return false; if (upd) { Client::self()->updateTableRows("messages",upd,false,wnd); - addTrayIcon("notification"); + addTrayIcon(notif); } else if (!show) - removeTrayIcon("notification"); + removeTrayIcon(notif); NamedList p(""); const char* ok = String::boolText(show); p.addParam("check:messages_show",ok); @@ -8666,6 +8683,78 @@ bool DefaultLogic::showNotificationArea(bool show, Window* wnd, NamedList* upd) return true; } +// Show a roster change or failure notification +void DefaultLogic::showUserRosterNotification(ClientAccount* a, const String& oper, + Message& msg, const String& contactUri, bool newContact) +{ + if (!a) + return; + NamedList list(""); + NamedList* upd = 0; + String text; + const char* firstButton = 0; + bool update = (oper == "update"); + const char* notif = "notification"; + ClientContact* c = contactUri ? a->findContactByUri(contactUri) : 0; + String cName; + if (c) + buildContactName(cName,*c); + else + cName = contactUri; + if (update || oper == "delete") { + if (!c) + return; + notif = "info"; + upd = buildNotifArea(list,"generic",a->toString(),contactUri, + "Friends list changed"); + text << (update ? (newContact ? "Added" : "Updated") : "Removed"); + text << " friend " << cName; + } + else if (oper == "error") { + if (!contactUri) + return; + ClientContact* c = a->findContactByUri(contactUri); + const String& req = msg["requested_operation"]; + const char* what = 0; + if (req == "update") { + upd = buildNotifArea(list,"contactupdatefail",a->toString(), + contactUri,"Friend update failure"); + what = (c ? "update" : "add"); + } + else if (req == "delete") { + if (!c) + return; + upd = buildNotifArea(list,"contactremovefail",a->toString(), + contactUri,"Friend delete failure"); + what = "remove"; + } + else + return; + text << "Failed to " << what << " friend " << cName; + addError(text,msg); + } + else if (oper == "queryerror") { + upd = buildNotifArea(list,"rosterreqfail",a->toString(),String::empty(), + "Friends list failure"); + firstButton = "Retry"; + text << "Failed to retrieve the friends list"; + addError(text,msg); + } + else { + if (oper == "result") + Debug(ClientDriver::self(),DebugAll,"Contact %s for '%s' account=%s confirmed", + msg.getValue("requested_operation"),msg.getValue("contact"), + a->toString().c_str()); + return; + } + setGenericNotif(*upd,firstButton); + Debug(ClientDriver::self(),DebugAll,"Account '%s'. %s", + a->toString().c_str(),text.c_str()); + text << "\r\nAccount: " << a->toString(); + upd->addParam("text",text); + showNotificationArea(true,Client::self()->getWindow(s_wndMain),&list,notif); +} + // Handle actions from notification area. Return true if handled bool DefaultLogic::handleNotificationAreaAction(const String& action, Window* wnd) { diff --git a/share/skins/default/qt4client.ui b/share/skins/default/qt4client.ui index 12f64a5a..0ef89ee6 100644 --- a/share/skins/default/qt4client.ui +++ b/share/skins/default/qt4client.ui @@ -1988,6 +1988,8 @@ QTextEdit { property:_yate_itemui=incomingfile:messages_okrejignore.ui property:_yate_itemui=rosterreqfail:messages_generic.ui property:_yate_itemui=noaudio:messages_generic.ui + property:_yate_itemui=contactupdatefail:messages_generic.ui + property:_yate_itemui=contactremovefail:messages_generic.ui diff --git a/share/skins/default/tray_info.png b/share/skins/default/tray_info.png new file mode 100644 index 00000000..b71dcbad Binary files /dev/null and b/share/skins/default/tray_info.png differ diff --git a/yatecbase.h b/yatecbase.h index aafd5cbc..8f03f515 100644 --- a/yatecbase.h +++ b/yatecbase.h @@ -822,6 +822,7 @@ public: */ enum TrayIconType { TrayIconMain = 0, + TrayIconInfo = 1000, TrayIconIncomingChat = 3000, TrayIconNotification = 5000, TrayIconIncomingCall = 10000, @@ -3226,8 +3227,13 @@ private: bool handleMucResNotify(Message& msg, ClientAccount* acc, const String& contact, const String& instance, const String& operation); // Show/hide the notification area (messages). - // Update rows if requested - bool showNotificationArea(bool show, Window* wnd, NamedList* upd = 0); + // Update rows if requested. Add/remove tray notification/info icon + bool showNotificationArea(bool show, Window* wnd, NamedList* upd = 0, + const char* notif = "notification"); + // Show a roster change or failure notification + void showUserRosterNotification(ClientAccount* a, const String& oper, + Message& msg, const String& contactUri = String::empty(), + bool newContact = true); // Handle actions from notification area. Return true if handled bool handleNotificationAreaAction(const String& action, Window* wnd); // Save a contact to config. Save chat rooms if the contact is a chat room