Clients/Client2018/content/internal/Chat/Modules/LuaChat/RobloxEventReceiver.lua

340 lines
12 KiB
Lua

local CoreGui = game:GetService("CoreGui")
local HttpService = game:GetService("HttpService")
local NotificationService = game:GetService("NotificationService")
local GuiService = game:GetService("GuiService")
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Modules = CoreGui.RobloxGui.Modules
local LuaChat = Modules.LuaChat
local LuaApp = Modules.LuaApp
local Constants = require(LuaChat.Constants)
local WebApi = require(LuaChat.WebApi)
local ConversationActions = require(LuaChat.Actions.ConversationActions)
local FetchChatEnabled = require(LuaChat.Actions.FetchChatEnabled)
local ReceivedUserTyping = require(LuaChat.Actions.ReceivedUserTyping)
local PlayTogetherActions = require(LuaChat.Actions.PlayTogetherActions)
local DialogInfo = require(LuaChat.DialogInfo)
local Config = require(LuaApp.Config)
local ToastModel = require(LuaChat.Models.ToastModel)
local NotificationType = require(LuaApp.Enum.NotificationType)
local ChangedParticipants = require(LuaChat.Actions.ChangedParticipants)
local PopRoute = require(LuaChat.Actions.PopRoute)
local RemovedConversation = require(LuaChat.Actions.RemovedConversation)
local RenamedGroupConversation = require(LuaChat.Actions.RenamedGroupConversation)
local SetChatEnabled = require(LuaChat.Actions.SetChatEnabled)
local SetConnectionState = require(LuaChat.Actions.SetConnectionState)
local SetRoute = require(LuaChat.Actions.SetRoute)
local ShowToast = require(LuaChat.Actions.ShowToast)
local SetPreloading = require(LuaApp.Actions.SetPreloading)
local FlagSettings = require(LuaApp.FlagSettings)
local isLuaAppFriendshipCreatedSignalREnabled = FlagSettings.IsLuaAppFriendshipCreatedSignalREnabled()
local luaChatDisconnectBackButtonWhenOffScreen = settings():GetFFlag("LuaChatDisconnectBackButtonWhenOffScreen")
local Intent = DialogInfo.Intent
local function jsonDecode(data)
return HttpService:JSONDecode(data)
end
local function getNewestWithNilPreviousMessageId(messages)
for id, message, _ in messages:CreateReverseIterator() do
if message.previousMessageId == nil then
return id
end
end
return messages.keys[1]
end
local RobloxEventReceiver = {}
function RobloxEventReceiver:init(store)
local function onChatNotifications(eventData)
local detail = jsonDecode(eventData.detail)
local detailType = detail.Type or eventData.detailType
if detailType == "RemovedFromConversation" then
local conversationId = tostring(detail.ConversationId)
store:dispatch(RemovedConversation(conversationId))
local chatReducer = store:getState().ChatAppReducer
if chatReducer and chatReducer.Location then
local currentLocation = chatReducer.Location.current
if currentLocation and currentLocation.parameters then
if currentLocation.parameters.conversationId == conversationId then
local messageKey = "Feature.Chat.Message.RemovedFromConversation"
local toastModel = ToastModel.new(Constants.ToastIDs.REMOVED_FROM_CONVERSATION, messageKey, {})
store:dispatch(ShowToast(toastModel))
end
end
end
elseif detailType == "ConversationRemoved" then
local conversationId = tostring(detail.ConversationId)
store:dispatch(RemovedConversation(conversationId))
elseif detailType == "ConversationTitleChanged" then
local conversationId = tostring(detail.ConversationId)
spawn(function()
local status, result = WebApi.GetConversations({conversationId})
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->ConversationTitleChanged")
return
end
local conversations = result.conversations
if #conversations > 0 then
local conversation = conversations[1]
local title = conversation.title
local isDefaultTitle = conversation.isDefaultTitle
store:dispatch(
RenamedGroupConversation(conversationId, title, isDefaultTitle, conversation.lastUpdated)
)
end
end)
elseif detailType == "ParticipantAdded" then
local convoId = tostring(detail.ConversationId)
spawn(function()
local status, result = WebApi.GetConversations({convoId})
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->ParticipantAdded")
return
end
local conversations = result.conversations
if #conversations > 0 then
local conversation = conversations[1]
local participants = conversation.participants
local title = conversation.title
store:dispatch(ChangedParticipants(convoId, participants, title, conversation.lastUpdated))
end
end)
elseif detailType == "ParticipantLeft" then
local convoId = tostring(detail.ConversationId)
spawn(function()
local status, result = WebApi.GetConversations({convoId})
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->ParticipantLeft", status)
return
end
local conversations = result.conversations
if #conversations > 0 then
local conversation = conversations[1]
local participants = conversation.participants
local title = conversation.title
store:dispatch(ChangedParticipants(convoId, participants, title, conversation.lastUpdated))
end
end)
elseif detailType == "AddedToConversation" then
local conversationId = tostring(detail.ConversationId)
spawn(function()
local status = store:dispatch(ConversationActions.GetConversations(conversationId))
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->AddedToConversation")
return
end
end)
elseif detailType == "NewConversation" then
local conversationId = tostring(detail.ConversationId)
spawn(function()
local status = store:dispatch(ConversationActions.GetConversations(conversationId))
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->NewConversation")
return
end
end)
elseif detailType == "NewMessage" or detailType == "NewMessageBySelf" then
local newMessageNotificationReceivedLocalTime = tick()
local conversationId = tostring(detail.ConversationId)
store:dispatch(
ConversationActions.GetNewMessages(
conversationId,
detailType == "NewMessageBySelf",
newMessageNotificationReceivedLocalTime
)
)
elseif detailType == "ParticipantTyping" then
local conversationId = tostring(detail.ConversationId)
local userId = tostring(detail.UserId)
store:dispatch(ReceivedUserTyping(conversationId, userId))
elseif detailType == "ConversationUniverseChanged" then
local conversationId = tostring(detail.ConversationId)
local universeId = detail.UniverseId and tostring(detail.UniverseId)
local rootPlaceId = detail.RootPlaceId and tostring(detail.RootPlaceId)
store:dispatch(PlayTogetherActions.SetPinnedGameForConversation(universeId, rootPlaceId, conversationId))
end
end
local function onPresenceNotifications(eventData)
local detail = jsonDecode(eventData.detail)
local userId = tostring(detail.UserId)
store:dispatch(ConversationActions.GetUserPresences({userId}))
end
local function onPresenceBulkNotifications(eventData)
local detail = jsonDecode(eventData.detail)
local userIds = {}
for _, update in ipairs(detail) do
table.insert(userIds, tostring(update.UserId))
end
store:dispatch(ConversationActions.GetUserPresences(userIds))
end
local function onAppShellNotifications(eventData)
-- Note: AppShellNotifications are local and don't come from the
-- network. eventData.detail is not a structure, unlike other messages.
local detailType = eventData.detailType
if detailType == "StartConversationWithUserId" then
local userId = eventData.detail
spawn(function()
local status, result = WebApi.StartOneToOneConversation(userId)
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->AppShellNotifications, Status: "..tostring(status) )
return
end
if store:getState().ChatAppReducer.Conversations[result.id] == nil then
--Call GetConversations to make sure we hit the user and presence
--endpoints if need be. Being a bit lazy I suppose
local status = store:dispatch(
ConversationActions.GetConversations({result.id})
)
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->StartConversationWithUserId, Status: "..tostring(status))
return
end
end
store:dispatch(SetRoute(Intent.Conversation, {conversationId = result.id}, Intent.ConversationHub))
end)
elseif detailType == "StartConversationWithId" then
local convoId = eventData.detail
if store:getState().ChatAppReducer.Conversations[convoId] == nil then
local status = store:dispatch(
ConversationActions.GetConversations({convoId})
)
if status ~= WebApi.Status.OK then
warn("WebApi failure in RobloxEventReceiver->StartConversationWithId, Status: "..tostring(status))
return
end
end
store:dispatch(SetRoute(Intent.Conversation, {conversationId = convoId}, Intent.ConversationHub))
elseif detailType == "Preloading" then
local isPreloading = eventData.detail == "true"
store:dispatch(SetPreloading(isPreloading))
end
end
local function onPrivacyNotifications(eventData)
local detail = jsonDecode(eventData.detail)
local detailType = detail.Type
if detailType == "ChatDisabled" then
store:dispatch(SetChatEnabled(false))
elseif detailType == "ChatEnabled" then
store:dispatch(SetChatEnabled(true))
end
end
local function onFriendshipNotifications(eventData)
local detail = jsonDecode(eventData.detail)
local detailType = detail.Type
if detailType == "FriendshipCreated" then
-- LuaApp's RobloxEventReceiver will create the new user and mock conversation if this flag is on
if not isLuaAppFriendshipCreatedSignalREnabled then
local userId = tostring(Players.LocalPlayer.UserId) == tostring(detail.EventArgs.UserId1)
and detail.EventArgs.UserId2 or detail.EventArgs.UserId1
store:dispatch(ConversationActions.FriendshipCreated(tostring(userId)))
end
end
end
local function onRobloxEventReceived(eventData)
if eventData.namespace == "ChatNotifications" then
onChatNotifications(eventData)
elseif eventData.namespace == "PresenceNotifications" then
onPresenceNotifications(eventData)
elseif eventData.namespace == "PresenceBulkNotifications" then
onPresenceBulkNotifications(eventData)
elseif eventData.namespace == "ChatPrivacySettingNotifications" then
onPrivacyNotifications(eventData)
elseif eventData.namespace == "AppShellNotifications" then
onAppShellNotifications(eventData)
elseif eventData.namespace == "FriendshipNotifications" then
onFriendshipNotifications(eventData)
end
end
local lastSeqNum = nil
local function onRobloxConnectionChanged(connectionHubName, connectionState, seqNum)
if connectionHubName == "signalR" then
store:dispatch(SetConnectionState(connectionState))
if connectionState == Enum.ConnectionState.Connected then
if seqNum ~= lastSeqNum then
store:dispatch(FetchChatEnabled(function(chatEnabled)
if chatEnabled then
store:dispatch(ConversationActions.RefreshConversations())
spawn(function()
store:dispatch(ConversationActions.GetAllFriendsAsync())
end)
store:dispatch(ConversationActions.GetAllUserPresences())
end
end))
lastSeqNum = seqNum
end
local conversations = store:getState().ChatAppReducer.Conversations
for conversationId, conversation in pairs(conversations) do
if conversation.fetchingOlderMessages then
local messages = conversation.messages
local exclusiveMessageStartId = getNewestWithNilPreviousMessageId(messages)
store:dispatch(ConversationActions.GetOlderMessages(conversationId, exclusiveMessageStartId))
end
end
end
end
end
local function onBackButtonPressed()
if #store:getState().ChatAppReducer.Location.history > 1 then
store:dispatch(PopRoute())
else
GuiService:BroadcastNotification("", NotificationType.BACK_BUTTON_NOT_CONSUMED)
end
end
--Protect this call because Tests run in a downgraded security context
pcall(function()
NotificationService.RobloxEventReceived:Connect(onRobloxEventReceived)
NotificationService.RobloxConnectionChanged:Connect(onRobloxConnectionChanged)
if not luaChatDisconnectBackButtonWhenOffScreen then
GuiService.ShowLeaveConfirmation:Connect(onBackButtonPressed)
end
end)
if Config.LuaChat.Debug then
UserInputService.InputEnded:Connect(function(input, gameProcessed)
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.Left then
onBackButtonPressed()
end
end
end)
end
end
return RobloxEventReceiver