diff options
| author | Callum Prentice <callum@lindenlab.com> | 2025-10-08 18:58:27 -0700 |
|---|---|---|
| committer | Callum Prentice <callum@lindenlab.com> | 2025-10-08 18:58:27 -0700 |
| commit | 0a14318dd6cb9e2eae142956126cfd000c3027d9 (patch) | |
| tree | 5eb7390e3e23460cb39e8055226bfbca00bce545 /indra | |
| parent | c257304ae73ee1760b505c87e6d8d1a394fcd02b (diff) | |
First commit for a second experiment, investigating how nearby voice/chat moderation might work
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/llfloaterimcontainer.cpp | 96 | ||||
| -rw-r--r-- | indra/newview/llfloaterimcontainer.h | 2 | ||||
| -rw-r--r-- | indra/newview/llnearbyvoicemoderation.cpp | 102 | ||||
| -rw-r--r-- | indra/newview/llnearbyvoicemoderation.h | 41 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/menu_conversation.xml | 105 |
6 files changed, 291 insertions, 57 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c727d5ae57..239193001c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -414,6 +414,7 @@ set(viewer_SOURCE_FILES llfloaterimnearbychat.cpp llfloaterimnearbychathandler.cpp llfloaterimnearbychatlistener.cpp + llnearbyvoicemoderation.cpp llnetmap.cpp llnotificationalerthandler.cpp llnotificationgrouphandler.cpp @@ -1087,6 +1088,7 @@ set(viewer_HEADER_FILES llnameeditor.h llnamelistctrl.h llnavigationbar.h + llnearbyvoicemoderation.h llnetmap.h llnotificationhandler.h llnotificationlistitem.h diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index d821d9a4a5..9a016f6286 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -57,6 +57,8 @@ #include "llsdserialize.h" #include "llviewermenu.h" // is_agent_mappable #include "llviewerobjectlist.h" +#include "llvoavatar.h" +#include "llnearbyvoicemoderation.h" const S32 EVENTS_PER_IDLE_LOOP_CURRENT_SESSION = 80; @@ -502,12 +504,13 @@ void LLFloaterIMContainer::idleUpdate() const LLConversationItem *current_session = getCurSelectedViewModelItem(); if (current_session) { - if (current_session->getType() == LLConversationItem::CONV_SESSION_GROUP) + bool is_nearby_chat = current_session->getType() == LLConversationItem::CONV_SESSION_NEARBY; + if (current_session->getType() == LLConversationItem::CONV_SESSION_GROUP || is_nearby_chat) { // Update moderator options visibility LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = current_session->getChildrenBegin(); LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = current_session->getChildrenEnd(); - bool is_moderator = isGroupModerator(); + bool is_moderator = isGroupModerator() || (is_nearby_chat && isNearbyChatModerator()); bool can_ban = haveAbilityToBan(); while (current_participant_model != end_participant_model) { @@ -1685,6 +1688,10 @@ bool LLFloaterIMContainer::visibleContextMenuItem(const LLSD& userdata) { return isMuted(conversation_item->getUUID()); } + else if ("can_allow_text_chat" == item) + { + return !isNearbyChatSpeakerSelected(); + } return true; } @@ -2009,9 +2016,27 @@ LLConversationViewParticipant* LLFloaterIMContainer::createConversationViewParti bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& userdata, bool is_self) { - // only group moderators can perform actions related to this "enable callback" - if (!isGroupModerator()) + if (isNearbyChatModerator() && isNearbyChatSpeakerSelected()) + { + // Determine here which actions are allowed + if ("can_moderate_voice" == userdata) + { + return true; + } + else if (("can_mute" == userdata)) + { + return true; + } + else if ("can_unmute" == userdata) + { + return true; + } + + return false; + } + else if (!isGroupModerator()) { + // only group moderators can perform actions related to this "enable callback" return false; } @@ -2144,7 +2169,37 @@ void LLFloaterIMContainer::banSelectedMember(const LLUUID& participant_uuid) void LLFloaterIMContainer::moderateVoice(const std::string& command, const LLUUID& userID) { - if (!gAgent.getRegion()) return; + if (!gAgent.getRegion()) + { + return; + } + + if (isNearbyChatSpeakerSelected()) + { + if ("selected" == command) + { + // Toggle the voice icon display + LLAvatarActions::toggleMuteVoice(userID); + + // Request a mute/unmute using a capability request via the simulator + const bool mute_state = LLAvatarActions::isVoiceMuted(userID); + LLNearbyVoiceModeration::getInstance()->requestMuteChange(userID, mute_state); + } + else + if ("mute_all" == command) + { + // TODO: the SpatialVoiceModerationRequest has an mute_all/unmute_all + // verb but we do not have an equivalent of LLAvatarActions::toggleMuteVoice(userID); + // to visually mute all the speaker icons in the conversation floater + } + else + if ("unmute_all" == command) + { + // TODO: same idea as "mute_all" above + } + + return; + } if (command.compare("selected")) { @@ -2262,6 +2317,37 @@ LLSpeaker * LLFloaterIMContainer::getSpeakerOfSelectedParticipant(LLSpeakerMgr * return speaker_managerp->findSpeaker(participant_itemp->getUUID()); } +bool LLFloaterIMContainer::isNearbyChatSpeakerSelected() +{ + LLFolderViewItem *selectedItem = mConversationsRoot->getCurSelectedItem(); + if (NULL == selectedItem) + { + LL_WARNS() << "Current selected item is null" << LL_ENDL; + return NULL; + } + + conversations_widgets_map::const_iterator iter = mConversationsWidgets.begin(); + conversations_widgets_map::const_iterator end = mConversationsWidgets.end(); + const LLUUID * conversation_uuidp = NULL; + while(iter != end) + { + if (iter->second == selectedItem || iter->second == selectedItem->getParentFolder()) + { + conversation_uuidp = &iter->first; + break; + } + ++iter; + } + // Nearby chat ID is LLUUID::null + return conversation_uuidp->isNull(); +} + +bool LLFloaterIMContainer::isNearbyChatModerator() +{ + // TODO: Need a better heurestic for determining if this person is a moderator :) + return true; +} + void LLFloaterIMContainer::toggleAllowTextChat(const LLUUID& participant_uuid) { LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant()); diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h index 30eed8be36..c91093c107 100644 --- a/indra/newview/llfloaterimcontainer.h +++ b/indra/newview/llfloaterimcontainer.h @@ -178,6 +178,8 @@ private: void banSelectedMember(const LLUUID& participant_uuid); void openNearbyChat(); bool isParticipantListExpanded(); + bool isNearbyChatSpeakerSelected(); + bool isNearbyChatModerator(); void idleUpdate(); // for convenience (self) from static idle void idleProcessEvents(); diff --git a/indra/newview/llnearbyvoicemoderation.cpp b/indra/newview/llnearbyvoicemoderation.cpp new file mode 100644 index 0000000000..d714fc36b4 --- /dev/null +++ b/indra/newview/llnearbyvoicemoderation.cpp @@ -0,0 +1,102 @@ +/** + * @file llnearbyvoicemoderation.cpp + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llviewerregion.h" +#include "llvoavatar.h" +#include "llviewerobjectlist.h" + +#include "llnearbyvoicemoderation.h" + +LLVOAvatar* LLNearbyVoiceModeration::getVOAvatarFromId(const LLUUID& agent_id) +{ + LLViewerObject *obj = gObjectList.findObject(agent_id); + while (obj && obj->isAttachment()) + { + obj = (LLViewerObject*)obj->getParent(); + } + + if (obj && obj->isAvatar()) + { + return (LLVOAvatar*)obj; + } + else + { + return NULL; + } +} + +void LLNearbyVoiceModeration::requestMuteChange(const LLUUID& agent_id, bool mute) +{ + LLVOAvatar* avatar = getVOAvatarFromId(agent_id); + if (avatar) + { + LLViewerRegion* region = avatar->getRegion(); + if (! region || ! region->capabilitiesReceived()) + { + // TODO: Retry if fails since the capabilities may not have been received + // if this is called early into a region entry + LL_INFOS() << "Region or region capabilities unavailable" << LL_ENDL; + return; + } + LL_INFOS() << "Region name is " << region->getName() << LL_ENDL; + + std::string url = region->getCapability("SpatialVoiceModerationRequest"); + if (url.empty()) + { + // TODO: Retry if fails since URL may not have not be available + // if this is called early into a region entry + LL_INFOS() << "Capability URL is empty" << LL_ENDL; + return; + } + LL_INFOS() << "Capability URL is " << url << LL_ENDL; + + const std::string agent_name = avatar->getFullname(); + + const std::string operand = mute ? "mute" : "unmute"; + + LLSD body; + body["operand"] = operand; + body["agent_id"] = agent_id; + body["moderator_id"] = gAgent.getID(); + + LL_INFOS() << "Resident " << agent_name + << " (" << agent_id << ")" << " applying " << operand << LL_ENDL; + + std::string success_msg = + STRINGIZE("Resident " << agent_name + << " (" << agent_id << ")" << " nearby voice was set to " << operand); + + std::string failure_msg = + STRINGIZE("Unable to change voice muting for resident " + << agent_name << " (" << agent_id << ")"); + + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + success_msg, + failure_msg); + } +} diff --git a/indra/newview/llnearbyvoicemoderation.h b/indra/newview/llnearbyvoicemoderation.h new file mode 100644 index 0000000000..4275754dbf --- /dev/null +++ b/indra/newview/llnearbyvoicemoderation.h @@ -0,0 +1,41 @@ +/** + * @file llnearbyvoicemoderation.h + * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#pragma once + +class LLVOAvatar; + +class LLNearbyVoiceModeration : + public LLSingleton <LLNearbyVoiceModeration> { + LLSINGLETON(LLNearbyVoiceModeration) { + }; + + ~LLNearbyVoiceModeration() { + }; + + public: + LLVOAvatar* getVOAvatarFromId(const LLUUID& id); + void requestMuteChange(const LLUUID& userID, bool mute); +}; diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml index 62cdaa5886..cf9b8959ce 100644 --- a/indra/newview/skins/default/xui/en/menu_conversation.xml +++ b/indra/newview/skins/default/xui/en/menu_conversation.xml @@ -176,57 +176,58 @@ <on_click function="Group.DoToSelected" parameter="leave_group"/> <on_enable function="Avatar.EnableItem" parameter="can_leave_group" /> </menu_item_call> - <menu_item_separator layout="topleft" name="Moderator Options Separator"/> - <context_menu - label="Moderator Options" - layout="topleft" - name="Moderator Options"> - <menu_item_check - label="Allow text chat" - layout="topleft" - name="AllowTextChat"> - <on_check function="Avatar.CheckItem" parameter="is_allowed_text_chat" /> - <on_click function="Avatar.DoToSelected" parameter="toggle_allow_text_chat" /> - <on_enable function="Avatar.EnableItem" parameter="can_allow_text_chat" /> - </menu_item_check> - <menu_item_separator layout="topleft" name="moderate_voice_separator" /> - <menu_item_call - label="Mute this participant" - layout="topleft" - name="ModerateVoiceMuteSelected"> - <on_click function="Avatar.DoToSelected" parameter="selected" /> - <on_enable function="Avatar.EnableItem" parameter="can_mute" /> - <on_visible function="Avatar.VisibleItem" parameter="show_mute" /> - </menu_item_call> + <menu_item_separator layout="topleft" name="Moderator Options Separator"/> + <context_menu + label="Moderator Options" + layout="topleft" + name="Moderator Options"> + <menu_item_check + label="Allow text chat" + layout="topleft" + name="AllowTextChat"> + <on_check function="Avatar.CheckItem" parameter="is_allowed_text_chat" /> + <on_click function="Avatar.DoToSelected" parameter="toggle_allow_text_chat" /> + <on_enable function="Avatar.EnableItem" parameter="can_allow_text_chat" /> + <on_visible function="Avatar.VisibleItem" parameter="can_allow_text_chat" /> + </menu_item_check> + <menu_item_separator layout="topleft" name="moderate_voice_separator" /> + <menu_item_call + label="Mute this participant" + layout="topleft" + name="ModerateVoiceMuteSelected"> + <on_click function="Avatar.DoToSelected" parameter="selected" /> + <on_enable function="Avatar.EnableItem" parameter="can_mute" /> + <on_visible function="Avatar.VisibleItem" parameter="show_mute" /> + </menu_item_call> + <menu_item_call + label="Unmute this participant" + layout="topleft" + name="ModerateVoiceUnMuteSelected"> + <on_click function="Avatar.DoToSelected" parameter="selected" /> + <on_enable function="Avatar.EnableItem" parameter="can_unmute" /> + <on_visible function="Avatar.VisibleItem" parameter="show_unmute" /> + </menu_item_call> + <menu_item_call + label="Mute everyone" + layout="topleft" + name="ModerateVoiceMute"> + <on_click function="Avatar.DoToSelected" parameter="mute_all" /> + <on_enable function="Avatar.EnableItem" parameter="can_moderate_voice" /> + </menu_item_call> + <menu_item_call + label="Unmute everyone" + layout="topleft" + name="ModerateVoiceUnmute"> + <on_click function="Avatar.DoToSelected" parameter="unmute_all" /> + <on_enable function="Avatar.EnableItem" parameter="can_moderate_voice" /> + </menu_item_call> + </context_menu> + <menu_item_separator layout="topleft" name="Group Ban Separator"/> <menu_item_call - label="Unmute this participant" - layout="topleft" - name="ModerateVoiceUnMuteSelected"> - <on_click function="Avatar.DoToSelected" parameter="selected" /> - <on_enable function="Avatar.EnableItem" parameter="can_unmute" /> - <on_visible function="Avatar.VisibleItem" parameter="show_unmute" /> - </menu_item_call> - <menu_item_call - label="Mute everyone" - layout="topleft" - name="ModerateVoiceMute"> - <on_click function="Avatar.DoToSelected" parameter="mute_all" /> - <on_enable function="Avatar.EnableItem" parameter="can_moderate_voice" /> - </menu_item_call> - <menu_item_call - label="Unmute everyone" - layout="topleft" - name="ModerateVoiceUnmute"> - <on_click function="Avatar.DoToSelected" parameter="unmute_all" /> - <on_enable function="Avatar.EnableItem" parameter="can_moderate_voice" /> - </menu_item_call> - </context_menu> - <menu_item_separator layout="topleft" name="Group Ban Separator"/> - <menu_item_call - label="Ban member" - layout="topleft" - name="BanMember"> - <on_click function="Avatar.DoToSelected" parameter="ban_member" /> - <on_enable function="Avatar.EnableItem" parameter="can_ban_member" /> - </menu_item_call> + label="Ban member" + layout="topleft" + name="BanMember"> + <on_click function="Avatar.DoToSelected" parameter="ban_member" /> + <on_enable function="Avatar.EnableItem" parameter="can_ban_member" /> + </menu_item_call> </toggleable_menu> |
