Implemented EXT-6783(normal sub task) - Implement saving of unread notifications.

Utilized old save and load notification code.
Main concern was with notifications that have complex responder - UserGiveItem, ObjectGiveItem. Those responders are object with own fields that need to persist through sessions.

Notifications that should be saved are marked with persist="true" in notifications.xml
Notifications using functor responders are saved automatically.
Notifications using object responders need additional tuning. Responder object should be a) serializable(implement LLNotificationResponderInterface), b) registered with LLResponderRegistry.

At this point following notifications persist through sessions: UserGiveItem, ObjectGiveItem, TeleportOffered, FrienshipOffered.

Reviewed by Mike Antipov - https://codereview.productengine.com/secondlife/r/211/

--HG--
branch : notifications
meow-7.2.2
Dmitry Zaporozhan 2010-04-21 14:29:31 +03:00
parent 6b722079c4
commit a4e20e9932
13 changed files with 493 additions and 151 deletions

View File

@ -48,122 +48,38 @@
const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
// local channel for notification history
class LLNotificationHistoryChannel : public LLNotificationChannel
// Local channel for persistent notifications
// Stores only persistent notifications.
// Class users can use connectChanged() to process persistent notifications
// (see LLNotificationStorage for example).
class LLPersistentNotificationChannel : public LLNotificationChannel
{
LOG_CLASS(LLNotificationHistoryChannel);
LOG_CLASS(LLPersistentNotificationChannel);
public:
LLNotificationHistoryChannel(const std::string& filename) :
LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()),
mFileName(filename)
LLPersistentNotificationChannel() :
LLNotificationChannel("Persistent", "Visible", &notificationFilter, LLNotificationComparators::orderByUUID())
{
connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1));
loadPersistentNotifications();
}
private:
bool historyHandler(const LLSD& payload)
// The channel gets all persistent notifications except those that have been canceled
static bool notificationFilter(LLNotificationPtr pNotification)
{
// we ignore "load" messages, but rewrite the persistence file on any other
std::string sigtype = payload["sigtype"];
if (sigtype != "load")
{
savePersistentNotifications();
}
return false;
bool handle_notification = false;
handle_notification = pNotification->isPersistent()
&& !pNotification->isCancelled();
return handle_notification;
}
// The history channel gets all notifications except those that have been cancelled
static bool historyFilter(LLNotificationPtr pNotification)
{
return !pNotification->isCancelled();
}
void savePersistentNotifications()
{
/* NOTE: As of 2009-11-09 the reload of notifications on startup does not
work, and has not worked for months. Skip saving notifications until the
read can be fixed, because this hits the disk once per notification and
causes log spam. James
llinfos << "Saving open notifications to " << mFileName << llendl;
llofstream notify_file(mFileName.c_str());
if (!notify_file.is_open())
{
llwarns << "Failed to open " << mFileName << llendl;
return;
}
LLSD output;
output["version"] = NOTIFICATION_PERSIST_VERSION;
LLSD& data = output["data"];
for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
{
if (!LLNotifications::instance().templateExists((*it)->getName())) continue;
// only store notifications flagged as persisting
LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName());
if (!templatep->mPersist) continue;
data.append((*it)->asLLSD());
}
LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
*/
}
void loadPersistentNotifications()
{
llinfos << "Loading open notifications from " << mFileName << llendl;
llifstream notify_file(mFileName.c_str());
if (!notify_file.is_open())
{
llwarns << "Failed to open " << mFileName << llendl;
return;
}
LLSD input;
LLPointer<LLSDParser> parser = new LLSDXMLParser();
if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
{
llwarns << "Failed to parse open notifications" << llendl;
return;
}
if (input.isUndefined()) return;
std::string version = input["version"];
if (version != NOTIFICATION_PERSIST_VERSION)
{
llwarns << "Bad open notifications version: " << version << llendl;
return;
}
LLSD& data = input["data"];
if (data.isUndefined()) return;
LLNotifications& instance = LLNotifications::instance();
for (LLSD::array_const_iterator notification_it = data.beginArray();
notification_it != data.endArray();
++notification_it)
{
instance.add(LLNotificationPtr(new LLNotification(*notification_it)));
}
}
//virtual
void onDelete(LLNotificationPtr pNotification)
{
// we want to keep deleted notifications in our log
// we want to keep deleted notifications in our log, otherwise some
// notifications will be lost on exit.
mItems.insert(pNotification);
return;
}
private:
std::string mFileName;
};
bool filterIgnoredNotifications(LLNotificationPtr notification)
@ -417,6 +333,10 @@ LLNotification::LLNotification(const LLNotification::Params& p) :
mTemporaryResponder = true;
}
else if(p.functor.responder.isChosen())
{
mResponder = p.functor.responder;
}
if(p.responder.isProvided())
{
@ -462,6 +382,12 @@ LLSD LLNotification::asLLSD()
output["priority"] = (S32)mPriority;
output["responseFunctor"] = mResponseFunctorName;
output["reusable"] = mIsReusable;
if(mResponder)
{
output["responder"] = mResponder->asLLSD();
}
return output;
}
@ -571,12 +497,20 @@ void LLNotification::respond(const LLSD& response)
// *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
mRespondedTo = true;
mResponse = response;
// look up the functor
LLNotificationFunctorRegistry::ResponseFunctor functor =
LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
// and then call it
functor(asLLSD(), response);
if(mResponder)
{
mResponder->handleRespond(asLLSD(), response);
}
else
{
// look up the functor
LLNotificationFunctorRegistry::ResponseFunctor functor =
LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
// and then call it
functor(asLLSD(), response);
}
if (mTemporaryResponder && !isReusable())
{
LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
@ -621,6 +555,11 @@ void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::Res
LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
}
void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
{
mResponder = responder;
}
bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
{
for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin();
@ -1116,12 +1055,9 @@ void LLNotifications::createDefaultChannels()
LLNotificationChannel::buildChannel("Visible", "Ignore",
&LLNotificationFilters::includeEverything);
// create special history channel
//std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
// use ^^^ when done debugging notifications serialization
std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" );
// create special persistent notification channel
// this isn't a leak, don't worry about the empty "new"
new LLNotificationHistoryChannel(notifications_log_file);
new LLPersistentNotificationChannel();
// connect action methods to these channels
LLNotifications::instance().getChannel("Expiration")->

View File

@ -116,8 +116,23 @@ typedef enum e_notification_priority
NOTIFICATION_PRIORITY_CRITICAL
} ENotificationPriority;
class LLNotificationResponderInterface
{
public:
LLNotificationResponderInterface(){};
virtual ~LLNotificationResponderInterface(){};
virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0;
virtual LLSD asLLSD() = 0;
virtual void fromLLSD(const LLSD& params) = 0;
};
typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
typedef boost::shared_ptr<LLNotificationResponderInterface> LLNotificationResponderPtr;
typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
@ -303,10 +318,12 @@ public:
{
Alternative<std::string> name;
Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
Alternative<LLNotificationResponderPtr> responder;
Functor()
: name("functor_name"),
function("functor")
function("functor"),
responder("responder")
{}
};
Optional<Functor> functor;
@ -349,12 +366,13 @@ private:
bool mIgnored;
ENotificationPriority mPriority;
LLNotificationFormPtr mForm;
void* mResponderObj;
void* mResponderObj; // TODO - refactor/remove this field
bool mIsReusable;
LLNotificationResponderPtr mResponder;
// a reference to the template
LLNotificationTemplatePtr mTemplatep;
/*
We want to be able to store and reload notifications so that they can survive
a shutdown/restart of the client. So we can't simply pass in callbacks;
@ -393,6 +411,8 @@ public:
void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb);
void setResponseFunctor(const LLNotificationResponderPtr& responder);
typedef enum e_response_template_type
{
WITHOUT_DEFAULT_BUTTON,
@ -459,7 +479,12 @@ public:
{
return mTemplatep->mName;
}
bool isPersistent() const
{
return mTemplatep->mPersist;
}
const LLUUID& id() const
{
return mId;

View File

@ -294,6 +294,7 @@ set(viewer_SOURCE_FILES
llnotificationmanager.cpp
llnotificationofferhandler.cpp
llnotificationscripthandler.cpp
llnotificationstorage.cpp
llnotificationtiphandler.cpp
lloutputmonitorctrl.cpp
llpanelavatar.cpp
@ -795,6 +796,7 @@ set(viewer_HEADER_FILES
llnetmap.h
llnotificationhandler.h
llnotificationmanager.h
llnotificationstorage.h
lloutputmonitorctrl.h
llpanelavatar.h
llpanelavatartag.h

View File

@ -35,6 +35,7 @@
#include "llchannelmanager.h"
#include "llappviewer.h"
#include "llnotificationstorage.h"
#include "llviewercontrol.h"
#include "llviewerwindow.h"
#include "llrootview.h"
@ -107,31 +108,35 @@ void LLChannelManager::onLoginCompleted()
if(!away_notifications)
{
onStartUpToastClose();
return;
}
// create a channel for the StartUp Toast
LLChannelManager::Params p;
p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID"));
p.channel_align = CA_RIGHT;
mStartUpChannel = createChannel(p);
if(!mStartUpChannel)
else
{
onStartUpToastClose();
return;
// create a channel for the StartUp Toast
LLChannelManager::Params p;
p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID"));
p.channel_align = CA_RIGHT;
mStartUpChannel = createChannel(p);
if(!mStartUpChannel)
{
onStartUpToastClose();
}
else
{
gViewerWindow->getRootView()->addChild(mStartUpChannel);
// init channel's position and size
S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin");
S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth");
mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound);
mStartUpChannel->setMouseDownCallback(boost::bind(&LLNotificationWellWindow::onStartUpToastClick, LLNotificationWellWindow::getInstance(), _2, _3, _4));
mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this));
mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime"));
}
}
gViewerWindow->getRootView()->addChild(mStartUpChannel);
// init channel's position and size
S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin");
S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth");
mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound);
mStartUpChannel->setMouseDownCallback(boost::bind(&LLNotificationWellWindow::onStartUpToastClick, LLNotificationWellWindow::getInstance(), _2, _3, _4));
mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this));
mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime"));
LLPersistentNotificationStorage::getInstance()->loadNotifications();
}
//--------------------------------------------------------------------------

View File

@ -0,0 +1,228 @@
/**
* @file llnotificationstorage.cpp
* @brief LLPersistentNotificationStorage class implementation
*
* $LicenseInfo:firstyear=2010&license=viewergpl$
*
* Copyright (c) 2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h" // must be first include
#include "llnotificationstorage.h"
#include "llxmlnode.h" // for linux compilers
#include "llchannelmanager.h"
#include "llscreenchannel.h"
#include "llscriptfloater.h"
#include "llsdserialize.h"
#include "llviewermessage.h"
//////////////////////////////////////////////////////////////////////////
class LLResponderRegistry
{
public:
static void registerResponders();
static LLNotificationResponderInterface* createResponder(const std::string& notification_name, const LLSD& params);
private:
template<typename RESPONDER_TYPE>
static LLNotificationResponderInterface* create(const LLSD& params)
{
RESPONDER_TYPE* responder = new RESPONDER_TYPE();
responder->fromLLSD(params);
return responder;
}
typedef boost::function<LLNotificationResponderInterface* (const LLSD& params)> responder_constructor_t;
static void add(const std::string& notification_name, const responder_constructor_t& ctr);
private:
typedef std::map<std::string, responder_constructor_t> build_map_t;
static build_map_t sBuildMap;
};
//////////////////////////////////////////////////////////////////////////
LLPersistentNotificationStorage::LLPersistentNotificationStorage()
{
mFileName = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
}
bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload)
{
// we ignore "load" messages, but rewrite the persistence file on any other
const std::string sigtype = payload["sigtype"].asString();
if ("load" != sigtype)
{
saveNotifications();
}
return false;
}
void LLPersistentNotificationStorage::saveNotifications()
{
// TODO - think about save optimization.
llofstream notify_file(mFileName.c_str());
if (!notify_file.is_open())
{
llwarns << "Failed to open " << mFileName << llendl;
return;
}
LLSD output;
LLSD& data = output["data"];
LLNotificationChannelPtr history_channel = LLNotifications::instance().getChannel("Persistent");
LLNotificationSet::iterator it = history_channel->begin();
for ( ; history_channel->end() != it; ++it)
{
LLNotificationPtr notification = *it;
// After a notification was placed in Persist channel, it can become
// responded, expired - in this case we are should not save it
if(notification->isRespondedTo()
|| notification->isExpired())
{
continue;
}
data.append(notification->asLLSD());
}
LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
}
void LLPersistentNotificationStorage::loadNotifications()
{
LLResponderRegistry::registerResponders();
LLNotifications::instance().getChannel("Persistent")->
connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1));
llifstream notify_file(mFileName.c_str());
if (!notify_file.is_open())
{
llwarns << "Failed to open " << mFileName << llendl;
return;
}
LLSD input;
LLPointer<LLSDParser> parser = new LLSDXMLParser();
if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
{
llwarns << "Failed to parse open notifications" << llendl;
return;
}
if (input.isUndefined())
{
return;
}
LLSD& data = input["data"];
if (data.isUndefined())
{
return;
}
using namespace LLNotificationsUI;
LLScreenChannel* notification_channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->
findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
LLNotifications& instance = LLNotifications::instance();
for (LLSD::array_const_iterator notification_it = data.beginArray();
notification_it != data.endArray();
++notification_it)
{
LLSD notification_params = *notification_it;
LLNotificationPtr notification(new LLNotification(notification_params));
LLNotificationResponderPtr responder(LLResponderRegistry::
createResponder(notification_params["name"], notification_params["responder"]));
notification->setResponseFunctor(responder);
instance.add(notification);
// hide script floaters so they don't confuse the user and don't overlap startup toast
LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false);
if(notification_channel)
{
// hide saved toasts so they don't confuse the user
notification_channel->hideToast(notification->getID());
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
LLResponderRegistry::build_map_t LLResponderRegistry::sBuildMap;
void LLResponderRegistry::registerResponders()
{
sBuildMap.clear();
add("ObjectGiveItem", &create<LLOfferInfo>);
add("UserGiveItem", &create<LLOfferInfo>);
}
LLNotificationResponderInterface* LLResponderRegistry::createResponder(const std::string& notification_name, const LLSD& params)
{
build_map_t::const_iterator it = sBuildMap.find(notification_name);
if(sBuildMap.end() == it)
{
llwarns << "Responder for notification \'" << notification_name << "\' is not registered" << llendl;
return NULL;
}
responder_constructor_t ctr = it->second;
return ctr(params);
}
void LLResponderRegistry::add(const std::string& notification_name, const responder_constructor_t& ctr)
{
if(sBuildMap.find(notification_name) != sBuildMap.end())
{
llwarns << "Responder is already registered : " << notification_name << llendl;
llassert(!"Responder already registered");
}
sBuildMap[notification_name] = ctr;
}
// EOF

View File

@ -0,0 +1,65 @@
/**
* @file llnotificationstorage.h
* @brief LLNotificationStorage class declaration
*
* $LicenseInfo:firstyear=2010&license=viewergpl$
*
* Copyright (c) 2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_NOTIFICATIONSTORAGE_H
#define LL_NOTIFICATIONSTORAGE_H
#include "llnotifications.h"
// Class that saves not responded(unread) notifications.
// Unread notifications are saved in open_notifications.xml in SL account folder
//
// Notifications that should be saved(if unread) are marked with persist="true" in notifications.xml
// Notifications using functor responders are saved automatically (see llviewermessage.cpp
// lure_callback_reg for example).
// Notifications using object responders(LLOfferInfo) need additional tuning. Responder object should
// be a) serializable(implement LLNotificationResponderInterface),
// b) registered with LLResponderRegistry (found in llnotificationstorage.cpp).
class LLPersistentNotificationStorage : public LLSingleton<LLPersistentNotificationStorage>
{
LOG_CLASS(LLPersistentNotificationStorage);
public:
LLPersistentNotificationStorage();
void saveNotifications();
void loadNotifications();
private:
bool onPersistentChannelChanged(const LLSD& payload);
std::string mFileName;
};
#endif // LL_NOTIFICATIONSTORAGE_H

View File

@ -37,7 +37,7 @@
#include "lltimer.h"
#include "llvoiceclient.h"
struct LLOfferInfo;
class LLOfferInfo;
const S32 UPDATE_MEMBERS_PER_FRAME = 500;

View File

@ -539,4 +539,14 @@ bool LLScriptFloaterManager::getFloaterPosition(const LLUUID& object_id, Floater
return false;
}
void LLScriptFloaterManager::setFloaterVisible(const LLUUID& notification_id, bool visible)
{
LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>(
"script_floater", notification_id);
if(floater)
{
floater->setVisible(visible);
}
}
// EOF

View File

@ -99,6 +99,8 @@ public:
bool getFloaterPosition(const LLUUID& object_id, FloaterPositionInfo& fpi);
void setFloaterVisible(const LLUUID& notification_id, bool visible);
protected:
typedef std::map<std::string, EObjectType> object_type_map;

View File

@ -493,7 +493,7 @@ void LLToastNotifyPanel::onClickButton(void* data)
{
sButtonClickSignal(self->mNotification->getID(), button_name);
if(new_info)
if(new_info && !self->mNotification->isPersistent())
{
self->mNotification->setResponseFunctor(
boost::bind(&LLOfferInfo::inventory_offer_callback, new_info, _1, _2));

View File

@ -1244,6 +1244,16 @@ void inventory_offer_mute_callback(const LLUUID& blocked_id,
gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id));
}
LLOfferInfo::LLOfferInfo()
: LLNotificationResponderInterface()
, mFromGroup(FALSE)
, mFromObject(FALSE)
, mIM(IM_NOTHING_SPECIAL)
, mType(LLAssetType::AT_NONE)
, mPersist(false)
{
}
LLOfferInfo::LLOfferInfo(const LLSD& sd)
{
mIM = (EInstantMessage)sd["im_type"].asInteger();
@ -1257,6 +1267,7 @@ LLOfferInfo::LLOfferInfo(const LLSD& sd)
mFromName = sd["from_name"].asString();
mDesc = sd["description"].asString();
mHost = LLHost(sd["sender"].asString());
mPersist = sd["persist"].asBoolean();
}
LLOfferInfo::LLOfferInfo(const LLOfferInfo& info)
@ -1272,6 +1283,7 @@ LLOfferInfo::LLOfferInfo(const LLOfferInfo& info)
mFromName = info.mFromName;
mDesc = info.mDesc;
mHost = info.mHost;
mPersist = info.mPersist;
}
LLSD LLOfferInfo::asLLSD()
@ -1288,9 +1300,15 @@ LLSD LLOfferInfo::asLLSD()
sd["from_name"] = mFromName;
sd["description"] = mDesc;
sd["sender"] = mHost.getIPandPort();
sd["persist"] = mPersist;
return sd;
}
void LLOfferInfo::fromLLSD(const LLSD& params)
{
*this = params;
}
void LLOfferInfo::send_auto_receive_response(void)
{
LLMessageSystem* msg = gMessageSystem;
@ -1330,6 +1348,21 @@ void LLOfferInfo::send_auto_receive_response(void)
}
}
void LLOfferInfo::handleRespond(const LLSD& notification, const LLSD& response)
{
initRespondFunctionMap();
const std::string name = notification["name"].asString();
if(mRespondFunctions.find(name) == mRespondFunctions.end())
{
llwarns << "Unexpected notification name : " << name << llendl;
llassert(!"Unexpected notification name");
return;
}
mRespondFunctions[name](notification, response);
}
bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response)
{
LLChat chat;
@ -1466,7 +1499,10 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
gInventory.addObserver(opener);
}
delete this;
if(!mPersist)
{
delete this;
}
return false;
}
@ -1632,7 +1668,10 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const
gInventory.addObserver(opener);
}
delete this;
if(!mPersist)
{
delete this;
}
return false;
}
@ -1648,6 +1687,15 @@ protected:
}
};
void LLOfferInfo::initRespondFunctionMap()
{
if(mRespondFunctions.empty())
{
mRespondFunctions["ObjectGiveItem"] = boost::bind(&LLOfferInfo::inventory_task_offer_callback, this, _1, _2);
mRespondFunctions["UserGiveItem"] = boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2);
}
}
void inventory_offer_handler(LLOfferInfo* info)
{
//Until throttling is implmented, busy mode should reject inventory instead of silently
@ -1764,7 +1812,8 @@ void inventory_offer_handler(LLOfferInfo* info)
// Inventory Slurls don't currently work for non agent transfers, so only display the object name.
args["ITEM_SLURL"] = msg;
// Note: sets inventory_task_offer_callback as the callback
p.substitutions(args).payload(payload).functor.function(boost::bind(&LLOfferInfo::inventory_task_offer_callback, info, _1, _2));
p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info));
info->mPersist = true;
p.name = name_found ? "ObjectGiveItem" : "ObjectGiveItemUnknownUser";
// Pop up inv offer chiclet and let the user accept (keep), or reject (and silently delete) the inventory.
LLNotifications::instance().add(p);
@ -1776,7 +1825,8 @@ void inventory_offer_handler(LLOfferInfo* info)
// *TODO fix memory leak
// inventory_offer_callback() is not invoked if user received notification and
// closes viewer(without responding the notification)
p.substitutions(args).payload(payload).functor.function(boost::bind(&LLOfferInfo::inventory_offer_callback, info, _1, _2));
p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info));
info->mPersist = true;
p.name = "UserGiveItem";
// Prefetch the item into your local inventory.

View File

@ -40,6 +40,7 @@
#include "lluuid.h"
#include "message.h"
#include "stdenums.h"
#include "llnotifications.h"
//
// Forward declarations
@ -210,11 +211,10 @@ bool highlight_offered_item(const LLUUID& item_id);
void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid);
struct LLOfferInfo
class LLOfferInfo : public LLNotificationResponderInterface
{
LLOfferInfo()
: mFromGroup(FALSE), mFromObject(FALSE),
mIM(IM_NOTHING_SPECIAL), mType(LLAssetType::AT_NONE) {};
public:
LLOfferInfo();
LLOfferInfo(const LLSD& sd);
LLOfferInfo(const LLOfferInfo& info);
@ -232,12 +232,27 @@ struct LLOfferInfo
std::string mFromName;
std::string mDesc;
LLHost mHost;
bool mPersist;
// LLNotificationResponderInterface implementation
/*virtual*/ LLSD asLLSD();
/*virtual*/ void fromLLSD(const LLSD& params);
/*virtual*/ void handleRespond(const LLSD& notification, const LLSD& response);
LLSD asLLSD();
void send_auto_receive_response(void);
// TODO - replace all references with handleRespond()
bool inventory_offer_callback(const LLSD& notification, const LLSD& response);
bool inventory_task_offer_callback(const LLSD& notification, const LLSD& response);
private:
void initRespondFunctionMap();
typedef boost::function<bool (const LLSD&, const LLSD&)> respond_function_t;
typedef std::map<std::string, respond_function_t> respond_function_map_t;
respond_function_map_t mRespondFunctions;
};
void process_feature_disabled_message(LLMessageSystem* msg, void**);

View File

@ -5066,6 +5066,7 @@ No valid parcel could be found.
<notification
icon="notify.tga"
name="ObjectGiveItem"
persist="true"
type="offer">
An object named [OBJECTFROMNAME] owned by [NAME_SLURL] has given you this [OBJECTTYPE]:
[ITEM_SLURL]
@ -5110,6 +5111,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th
<notification
icon="notify.tga"
name="UserGiveItem"
persist="true"
type="offer">
[NAME_SLURL] has given you this [OBJECTTYPE]:
[ITEM_SLURL]
@ -5162,6 +5164,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th
<notification
icon="notify.tga"
name="TeleportOffered"
persist="true"
type="offer">
[NAME_SLURL] has offered to teleport you to their location:
@ -5207,6 +5210,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th
<notification
icon="notify.tga"
name="OfferFriendship"
persist="true"
type="offer">
[NAME_SLURL] is offering friendship.