CHUI-326 FIXED (One entry per conversation with a user in conversation log timestamped with most recent utterance/activity.)

- Modified LLConversationLog to show only one entry per conversation with user. I.e. there can't be two conversations with the same session_id in LLConversationLog.
- Got rid of processing voice sessions
- Refactored creation of conversation in LLConversationLog
- Refactored a little bit LLConversation and LLConversationLog: function names and made some functions private
meow-7.2.2
Paul ProductEngine 2012-09-11 17:45:49 +03:00
parent 20b95f6ac0
commit 1e2dcbfb3f
8 changed files with 187 additions and 99 deletions

View File

@ -37,7 +37,7 @@ struct Conversation_params
Conversation_params(time_t time)
: mTime(time),
mTimestamp(LLConversation::createTimestamp(time)),
mIsConversationPast(true)
mIsVoice(false)
{}
time_t mTime;
@ -48,7 +48,6 @@ struct Conversation_params
LLUUID mSessionID;
LLUUID mParticipantID;
bool mIsVoice;
bool mIsConversationPast;
bool mHasOfflineIMs;
};
@ -65,7 +64,6 @@ LLConversation::LLConversation(const Conversation_params& params)
mSessionID(params.mSessionID),
mParticipantID(params.mParticipantID),
mIsVoice(params.mIsVoice),
mIsConversationPast(params.mIsConversationPast),
mHasOfflineIMs(params.mHasOfflineIMs)
{
setListenIMFloaterOpened();
@ -80,7 +78,6 @@ LLConversation::LLConversation(const LLIMModel::LLIMSession& session)
mSessionID(session.mSessionID),
mParticipantID(session.mOtherParticipantID),
mIsVoice(session.mStartedAsIMCall),
mIsConversationPast(false),
mHasOfflineIMs(session.mHasOfflineMessage)
{
setListenIMFloaterOpened();
@ -96,7 +93,6 @@ LLConversation::LLConversation(const LLConversation& conversation)
mSessionID = conversation.getSessionID();
mParticipantID = conversation.getParticipantID();
mIsVoice = conversation.isVoice();
mIsConversationPast = conversation.isConversationPast();
mHasOfflineIMs = conversation.hasOfflineMessages();
setListenIMFloaterOpened();
@ -107,12 +103,10 @@ LLConversation::~LLConversation()
mIMFloaterShowedConnection.disconnect();
}
void LLConversation::setIsVoice(bool is_voice)
void LLConversation::updateTimestamp()
{
if (mIsConversationPast)
return;
mIsVoice = is_voice;
mTime = time_corrected();
mTimestamp = createTimestamp(mTime);
}
void LLConversation::onIMFloaterShown(const LLUUID& session_id)
@ -154,14 +148,18 @@ void LLConversation::setListenIMFloaterOpened()
LLIMFloater* floater = LLIMFloater::findInstance(mSessionID);
bool has_offline_ims = !mIsVoice && mHasOfflineIMs;
bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus();
bool offline_ims_visible = LLIMFloater::isVisible(floater) && floater->hasFocus();
// we don't need to listen for im floater with this conversation is opened
// if floater is already opened or this conversation doesn't have unread offline messages
if (has_offline_ims && !ims_are_read)
if (has_offline_ims && !offline_ims_visible)
{
mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1));
}
else
{
mHasOfflineIMs = false;
}
}
/************************************************************************/
@ -205,6 +203,7 @@ LLConversationLog::LLConversationLog()
if (ctrl->getValue().asBoolean())
{
LLIMMgr::instance().addSessionObserver(this);
newMessageSignalConnection = LLIMModel::instance().addNewMsgCallback(boost::bind(&LLConversationLog::onNewMessageReceived, this, _1));
}
}
@ -218,17 +217,80 @@ void LLConversationLog::observeIMSession()
if (gSavedPerAccountSettings.getBOOL("LogInstantMessages"))
{
LLIMMgr::instance().addSessionObserver(this);
LLIMModel::instance().addNewMsgCallback(boost::bind(&LLConversationLog::onNewMessageReceived, this, _1));
}
else
{
LLIMMgr::instance().removeSessionObserver(this);
newMessageSignalConnection.disconnect();
}
}
void LLConversationLog::logConversation(const LLConversation& conversation)
void LLConversationLog::logConversation(const LLUUID& session_id)
{
mConversations.push_back(conversation);
notifyObservers();
LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
LLConversation* conversation = findConversation(session_id);
if (session && conversation)
{
updateConversationTimestamp(conversation);
}
else if (session && !conversation)
{
createConversation(session_id);
}
}
void LLConversationLog::createConversation(const LLUUID& session_id)
{
LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
if (session)
{
LLConversation conversation(*session);
mConversations.push_back(conversation);
if (LLIMModel::LLIMSession::P2P_SESSION == session->mSessionType)
{
LLAvatarNameCache::get(session->mOtherParticipantID, boost::bind(&LLConversationLog::onAvatarNameCache, this, _1, _2, session));
}
notifyObservers();
}
}
void LLConversationLog::updateConversationName(const LLUUID& session_id, const std::string& name)
{
LLConversation* conversation = findConversation(session_id);
if (conversation)
{
conversation->setConverstionName(name);
notifyPrticularConversationObservers(session_id, LLConversationLogObserver::CHANGED_NAME);
}
}
void LLConversationLog::updateConversationTimestamp(LLConversation* conversation)
{
if (conversation)
{
conversation->updateTimestamp();
notifyPrticularConversationObservers(conversation->getSessionID(), LLConversationLogObserver::CHANGED_TIME);
}
}
LLConversation* LLConversationLog::findConversation(const LLUUID& session_id)
{
conversations_vec_t::iterator conv_it = mConversations.begin();
for(; conv_it != mConversations.end(); ++conv_it)
{
if (conv_it->getSessionID() == session_id)
{
return &*conv_it;
}
}
return NULL;
}
void LLConversationLog::removeConversation(const LLConversation& conversation)
@ -271,34 +333,7 @@ void LLConversationLog::removeObserver(LLConversationLogObserver* observer)
void LLConversationLog::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
{
LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
if (session)
{
if (LLIMModel::LLIMSession::P2P_SESSION == session->mSessionType)
{
LLAvatarNameCache::get(session->mOtherParticipantID, boost::bind(&LLConversationLog::onAvatarNameCache, this, _1, _2, session));
}
else
{
LLConversation conversation(*session);
LLConversationLog::instance().logConversation(conversation);
session->mVoiceChannel->setStateChangedCallback(boost::bind(&LLConversationLog::onVoiceChannelConnected, this, _5, _2));
}
}
}
void LLConversationLog::sessionRemoved(const LLUUID& session_id)
{
conversations_vec_t::reverse_iterator rev_iter = mConversations.rbegin();
for (; rev_iter != mConversations.rend(); ++rev_iter)
{
if (rev_iter->getSessionID() == session_id && !rev_iter->isConversationPast())
{
rev_iter->setIsPast(true);
return; // return here because only one session with session_id may be active
}
}
logConversation(session_id);
}
void LLConversationLog::cache()
@ -442,25 +477,13 @@ void LLConversationLog::notifyPrticularConversationObservers(const LLUUID& sessi
}
}
void LLConversationLog::onVoiceChannelConnected(const LLUUID& session_id, const LLVoiceChannel::EState& state)
void LLConversationLog::onNewMessageReceived(const LLSD& data)
{
conversations_vec_t::reverse_iterator rev_iter = mConversations.rbegin();
for (; rev_iter != mConversations.rend(); ++rev_iter)
{
if (rev_iter->getSessionID() == session_id && !rev_iter->isConversationPast() && LLVoiceChannel::STATE_CALL_STARTED == state)
{
rev_iter->setIsVoice(true);
notifyPrticularConversationObservers(session_id, LLConversationLogObserver::VOICE_STATE);
return; // return here because only one session with session_id may be active
}
}
const LLUUID session_id = data["session_id"].asUUID();
logConversation(session_id);
}
void LLConversationLog::onAvatarNameCache(const LLUUID& participant_id, const LLAvatarName& av_name, LLIMModel::LLIMSession* session)
{
LLConversation conversation(*session);
conversation.setConverstionName(av_name.getCompleteName());
LLConversationLog::instance().logConversation(conversation);
session->mVoiceChannel->setStateChangedCallback(boost::bind(&LLConversationLog::onVoiceChannelConnected, this, _5, _2));
updateConversationName(session->mSessionID, av_name.getCompleteName());
}

View File

@ -57,15 +57,18 @@ public:
const std::string& getTimestamp() const { return mTimestamp; }
const time_t& getTime() const { return mTime; }
bool isVoice() const { return mIsVoice; }
bool isConversationPast() const { return mIsConversationPast; }
bool hasOfflineMessages() const { return mHasOfflineIMs; }
void setIsVoice(bool is_voice);
void setIsPast (bool is_past) { mIsConversationPast = is_past; }
void setConverstionName(std::string conv_name) { mConversationName = conv_name; }
bool isOlderThan(U32 days) const;
/*
* updates last interaction time
*/
void updateTimestamp();
/*
* Resets flag of unread offline message to false when im floater with this conversation is opened.
*/
@ -86,7 +89,7 @@ private:
boost::signals2::connection mIMFloaterShowedConnection;
time_t mTime; // start time of conversation
time_t mTime; // last interaction time
SessionType mConversationType;
std::string mConversationName;
std::string mHistoryFileName;
@ -94,8 +97,7 @@ private:
LLUUID mParticipantID;
bool mIsVoice;
bool mHasOfflineIMs;
bool mIsConversationPast; // once session is finished conversation became past forever
std::string mTimestamp; // conversation start time in form of: mm/dd/yyyy hh:mm
std::string mTimestamp; // last interaction time in form of: mm/dd/yyyy hh:mm
};
/**
@ -112,32 +114,26 @@ class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObse
friend class LLSingleton<LLConversationLog>;
public:
/**
* adds conversation to the conversation list and notifies observers
*/
void logConversation(const LLConversation& conversation);
void removeConversation(const LLConversation& conversation);
/**
* Returns first conversation with matched session_id
*/
const LLConversation* getConversation(const LLUUID& session_id);
const LLConversation* getConversation(const LLUUID& session_id);
const std::vector<LLConversation>& getConversations() { return mConversations; }
void addObserver(LLConversationLogObserver* observer);
void removeObserver(LLConversationLogObserver* observer);
const std::vector<LLConversation>& getConversations() { return mConversations; }
// LLIMSessionObserver triggers
virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id);
virtual void sessionRemoved(const LLUUID& session_id);
virtual void sessionRemoved(const LLUUID& session_id){} // Stub
virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){}; // Stub
virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){}; // Stub
void notifyObservers();
void notifyPrticularConversationObservers(const LLUUID& session_id, U32 mask);
void onVoiceChannelConnected(const LLUUID& session_id, const LLVoiceChannel::EState& state);
void onNewMessageReceived(const LLSD& data);
/**
* public method which is called on viewer exit to save conversation log
@ -148,6 +144,13 @@ private:
LLConversationLog();
/**
* adds conversation to the conversation list and notifies observers
*/
void logConversation(const LLUUID& session_id);
void notifyPrticularConversationObservers(const LLUUID& session_id, U32 mask);
void observeIMSession();
/**
@ -161,11 +164,19 @@ private:
void onAvatarNameCache(const LLUUID& participant_id, const LLAvatarName& av_name, LLIMModel::LLIMSession* session);
void createConversation(const LLUUID& session_id);
void updateConversationTimestamp(LLConversation* conversation);
void updateConversationName(const LLUUID& session_id, const std::string& name);
LLConversation* findConversation(const LLUUID& session_id);
typedef std::vector<LLConversation> conversations_vec_t;
std::vector<LLConversation> mConversations;
std::set<LLConversationLogObserver*> mObservers;
LLFriendObserver* mFriendObserver; // Observer of the LLAvatarTracker instance
boost::signals2::connection newMessageSignalConnection;
};
class LLConversationLogObserver
@ -174,7 +185,8 @@ public:
enum EConversationChange
{
VOICE_STATE = 1
CHANGED_TIME = 1, // last interaction time changed
CHANGED_NAME = 2 // conversation name changed
};
virtual ~LLConversationLogObserver(){}

View File

@ -143,22 +143,32 @@ void LLConversationLogList::changed()
void LLConversationLogList::changed(const LLUUID& session_id, U32 mask)
{
if (mask & LLConversationLogObserver::VOICE_STATE)
LLConversationLogListItem* item = getConversationLogListItem(session_id);
if (!item)
{
std::vector<LLPanel*> panels;
LLFlatListViewEx::getItems(panels);
return;
}
std::vector<LLPanel*>::iterator iter = panels.begin();
if (mask & LLConversationLogObserver::CHANGED_TIME)
{
item->updateTimestamp();
for (; iter != panels.end(); ++iter)
// if list is sorted by date and a date of some item has changed,
// than the whole list should be rebuilt
if (E_SORT_BY_DATE == getSortOrder())
{
LLConversationLogListItem* item = dynamic_cast<LLConversationLogListItem*>(*iter);
if (item && session_id == item->getConversation()->getSessionID() && !item->getConversation()->isConversationPast())
{
item->initIcons();
return;
}
mIsDirty = true;
}
}
else if (mask & LLConversationLogObserver::CHANGED_NAME)
{
item->updateName();
// if list is sorted by name and a name of some item has changed,
// than the whole list should be rebuilt
if (E_SORT_BY_DATE == getSortOrder())
{
mIsDirty = true;
}
}
}
@ -401,6 +411,29 @@ const LLConversation* LLConversationLogList::getSelectedConversation()
return NULL;
}
LLConversationLogListItem* LLConversationLogList::getConversationLogListItem(const LLUUID& session_id)
{
std::vector<LLPanel*> panels;
LLFlatListViewEx::getItems(panels);
std::vector<LLPanel*>::iterator iter = panels.begin();
for (; iter != panels.end(); ++iter)
{
LLConversationLogListItem* item = dynamic_cast<LLConversationLogListItem*>(*iter);
if (item && session_id == item->getConversation()->getSessionID())
{
return item;
}
}
return NULL;
}
LLConversationLogList::ESortOrder LLConversationLogList::getSortOrder()
{
return static_cast<ESortOrder>(gSavedSettings.getU32("CallLogSortOrder"));
}
bool LLConversationLogListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const
{
const LLConversationLogListItem* conversation_item1 = dynamic_cast<const LLConversationLogListItem*>(item1);

View File

@ -43,6 +43,12 @@ class LLConversationLogList: public LLFlatListViewEx, public LLConversationLogOb
{
LOG_CLASS(LLConversationLogList);
public:
typedef enum e_sort_oder{
E_SORT_BY_NAME = 0,
E_SORT_BY_DATE = 1,
} ESortOrder;
struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params>
{
Params(){};
@ -90,6 +96,9 @@ private:
LLIMModel::LLIMSession::SType getSelectedSessionType();
const LLConversationLogListItem* getSelectedConversationPanel();
const LLConversation* getSelectedConversation();
LLConversationLogListItem* getConversationLogListItem(const LLUUID& session_id);
ESortOrder getSortOrder();
LLHandle<LLToggleableMenu> mContextMenu;
bool mIsDirty;

View File

@ -117,6 +117,16 @@ void LLConversationLogListItem::initIcons()
}
}
void LLConversationLogListItem::updateTimestamp()
{
mConversationDate->setValue(mConversation->getTimestamp());
}
void LLConversationLogListItem::updateName()
{
mConversationName->setValue(mConversation->getConversationName());
}
void LLConversationLogListItem::onMouseEnter(S32 x, S32 y, MASK mask)
{
getChildView("hovered_icon")->setVisible(true);

View File

@ -64,10 +64,16 @@ public:
void onDoubleClick();
void initIcons();
/**
* updates string value of last interaction time from conversation
*/
void updateTimestamp();
void updateName();
private:
void initIcons();
const LLConversation* mConversation;
LLTextBox* mConversationName;

View File

@ -45,11 +45,11 @@ BOOL LLFloaterConversationLog::postBuild()
switch (gSavedSettings.getU32("CallLogSortOrder"))
{
case E_SORT_BY_NAME:
case LLConversationLogList::E_SORT_BY_NAME:
mConversationLogList->sortByName();
break;
case E_SORT_BY_DATE:
case LLConversationLogList::E_SORT_BY_DATE:
mConversationLogList->sortByDate();
break;
}
@ -87,12 +87,12 @@ void LLFloaterConversationLog::onCustomAction (const LLSD& userdata)
if ("sort_by_name" == command_name)
{
mConversationLogList->sortByName();
gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_NAME);
gSavedSettings.setU32("CallLogSortOrder", LLConversationLogList::E_SORT_BY_NAME);
}
else if ("sort_by_date" == command_name)
{
mConversationLogList->sortByDate();
gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_DATE);
gSavedSettings.setU32("CallLogSortOrder", LLConversationLogList::E_SORT_BY_DATE);
}
else if ("sort_friends_on_top" == command_name)
{
@ -117,11 +117,11 @@ bool LLFloaterConversationLog::isActionChecked(const LLSD& userdata)
if ("sort_by_name" == command_name)
{
return sort_order == E_SORT_BY_NAME;
return sort_order == LLConversationLogList::E_SORT_BY_NAME;
}
else if ("sort_by_date" == command_name)
{
return sort_order == E_SORT_BY_DATE;
return sort_order == LLConversationLogList::E_SORT_BY_DATE;
}
else if ("sort_friends_on_top" == command_name)
{

View File

@ -34,11 +34,6 @@ class LLFloaterConversationLog : public LLFloater
{
public:
typedef enum e_sort_oder{
E_SORT_BY_NAME = 0,
E_SORT_BY_DATE = 1,
} ESortOrder;
LLFloaterConversationLog(const LLSD& key);
virtual ~LLFloaterConversationLog(){};