423 lines
14 KiB
Lua
423 lines
14 KiB
Lua
local CoreGui = game:GetService("CoreGui")
|
|
local GuiService = game:GetService("GuiService")
|
|
local PlayerService = game:GetService("Players")
|
|
|
|
local Modules = CoreGui.RobloxGui.Modules
|
|
|
|
local LuaApp = Modules.LuaApp
|
|
local LuaChat = Modules.LuaChat
|
|
|
|
local Create = require(LuaChat.Create)
|
|
local Constants = require(LuaChat.Constants)
|
|
local DialogInfo = require(LuaChat.DialogInfo)
|
|
local ConversationActions = require(LuaChat.Actions.ConversationActions)
|
|
local GetMultiplePlaceInfos = require(LuaChat.Actions.GetMultiplePlaceInfos)
|
|
local HeaderLoader = require(LuaChat.Components.HeaderLoader)
|
|
local PlaceInfoCard = require(LuaChat.Components.PlaceInfoCard)
|
|
local GameShareCard = require(LuaChat.Components.GameShareCard)
|
|
local ConversationList = require(LuaChat.Components.ConversationList)
|
|
local getInputEvent = require(LuaChat.Utils.getInputEvent)
|
|
local truncateAssetLink = require(LuaChat.Utils.truncateAssetLink)
|
|
local FlagSettings = require(LuaApp.FlagSettings)
|
|
|
|
local HttpStatus = require(LuaChat.WebApi).Status
|
|
|
|
local ToastModel = require(LuaChat.Models.ToastModel)
|
|
local ShowToast = require(LuaChat.Actions.ShowToast)
|
|
|
|
local NavigateBack = require(Modules.LuaApp.Thunks.NavigateBack)
|
|
local RemoveRoute = require(LuaChat.Actions.RemoveRoute)
|
|
local SetAppLoaded = require(LuaChat.Actions.SetAppLoaded)
|
|
|
|
local NotificationType = require(LuaApp.Enum.NotificationType)
|
|
|
|
local Intent = DialogInfo.Intent
|
|
|
|
local FFlagLuaChatToSplitRbxConnections = settings():GetFFlag("LuaChatToSplitRbxConnections")
|
|
local FFlagShareGameToChatStatusAnalytics = settings():GetFFlag("ShareGameToChatStatusAnalytics")
|
|
|
|
local GameShareComponent = {}
|
|
GameShareComponent.__index = GameShareComponent
|
|
|
|
local ICON_CELL_WIDTH = 60
|
|
local SEARCH_BOX_HEIGHT = 48
|
|
local CLEAR_TEXT_WIDTH = 44
|
|
local PLACE_INFO_FRAME_HEIGHT = 84
|
|
|
|
local function requestOlderConversations(appState)
|
|
-- Don't fetch older conversations if the oldest conversation has already been fetched.
|
|
if appState.store:getState().ChatAppReducer.ConversationsAsync.oldestConversationIsFetched then
|
|
return
|
|
end
|
|
|
|
-- Ask for new conversations
|
|
local convoCount = 0
|
|
for _, _ in pairs(appState.store:getState().ChatAppReducer.Conversations) do
|
|
convoCount = convoCount + 1
|
|
end
|
|
local pageSize = Constants.PageSize.GET_CONVERSATIONS
|
|
local currentPage = math.floor(convoCount / pageSize)
|
|
spawn(function()
|
|
appState.store:dispatch(ConversationActions.GetLocalUserConversationsAsync(currentPage + 1, pageSize))
|
|
end)
|
|
end
|
|
|
|
function GameShareComponent.new(appState, placeId, innerFrame)
|
|
local self = {}
|
|
self._analytics = appState.analytics
|
|
self.appState = appState
|
|
self.placeId = placeId
|
|
self.placeInfo = appState.store:getState().ChatAppReducer.PlaceInfos[placeId]
|
|
setmetatable(self, GameShareComponent)
|
|
|
|
-- Header
|
|
self.header = HeaderLoader.GetHeader(appState, Intent.GameShare)
|
|
self.header:SetDefaultSubtitle()
|
|
self.header:SetTitle(appState.localization:Format("Feature.Chat.Heading.ShareGameToChat"))
|
|
self.header:SetBackButtonEnabled(true)
|
|
|
|
-- Place Info Card Frame
|
|
self.placeInfoCardFrame = Create.new"Frame" {
|
|
Name = "PlaceInfoCardFrame",
|
|
BackgroundColor3 = Constants.Color.GRAY5,
|
|
BorderSizePixel = 0,
|
|
Size = UDim2.new(1, 0, 0, PLACE_INFO_FRAME_HEIGHT),
|
|
LayoutOrder = 2,
|
|
}
|
|
|
|
-- Search Container
|
|
self.searchContainer = Create.new"Frame" {
|
|
Name = "SearchContainer",
|
|
BackgroundTransparency = 0,
|
|
BackgroundColor3 = Constants.Color.WHITE,
|
|
BorderSizePixel = 0,
|
|
Size = UDim2.new(1, 0, 0, SEARCH_BOX_HEIGHT),
|
|
Visible = false,
|
|
LayoutOrder = 3,
|
|
|
|
Create.new"ImageLabel" {
|
|
Name = "SearchIcon",
|
|
BackgroundTransparency = 1,
|
|
Size = UDim2.new(0, 24, 0, 24),
|
|
Position = UDim2.new(0, ICON_CELL_WIDTH/2, 0.5, 0),
|
|
AnchorPoint = Vector2.new(0.5, 0.5),
|
|
ImageColor3 = Constants.Color.GRAY3,
|
|
Image = "rbxasset://textures/ui/LuaChat/icons/ic-search.png",
|
|
},
|
|
Create.new"TextBox" {
|
|
Name = "Search",
|
|
BackgroundTransparency = 1,
|
|
Size = UDim2.new(1, -CLEAR_TEXT_WIDTH-ICON_CELL_WIDTH, 1, 0),
|
|
Position = UDim2.new(0, ICON_CELL_WIDTH, 0, 0),
|
|
TextSize = Constants.Font.FONT_SIZE_18,
|
|
TextColor3 = Constants.Color.GRAY1,
|
|
Font = Enum.Font.SourceSans,
|
|
Text = "",
|
|
PlaceholderText = appState.localization:Format("Feature.Chat.Label.SearchForFriendsAndChat"),
|
|
PlaceholderColor3 = Constants.Color.GRAY3,
|
|
TextXAlignment = Enum.TextXAlignment.Left,
|
|
OverlayNativeInput = true,
|
|
ClearTextOnFocus = false,
|
|
ClipsDescendants = true,
|
|
},
|
|
Create.new"ImageButton" {
|
|
Name = "Clear",
|
|
BackgroundTransparency = 1,
|
|
Size = UDim2.new(0, 16, 0, 16),
|
|
Position = UDim2.new(1, -(CLEAR_TEXT_WIDTH/2), 0.5, 0),
|
|
AnchorPoint = Vector2.new(0.5, 0.5),
|
|
AutoButtonColor = false,
|
|
Image = "rbxasset://textures/ui/LuaChat/icons/ic-clear-solid.png",
|
|
ImageTransparency = 1,
|
|
},
|
|
}
|
|
self.clearSearchButton = self.searchContainer.Clear
|
|
self.searchBox = self.searchContainer.Search
|
|
self.SearchFilterPredicate = function(other)
|
|
if self.searchBox.Text == "" then
|
|
return true
|
|
end
|
|
return string.find(string.lower(other), string.lower(self.searchBox.Text), 1, true) ~= nil
|
|
end
|
|
|
|
local divider = Create.new"Frame" {
|
|
Name = "Divider",
|
|
BackgroundColor3 = Constants.Color.GRAY4,
|
|
BorderSizePixel = 0,
|
|
Size = UDim2.new(1, 0, 0, 1),
|
|
LayoutOrder = 4,
|
|
}
|
|
|
|
-- Conversation List Frame
|
|
self.conversationListFrame = Create.new "Frame" {
|
|
Name = "ConversationListFrame",
|
|
BackgroundTransparency = 1,
|
|
BorderSizePixel = 0,
|
|
LayoutOrder = 5,
|
|
Size = UDim2.new(1, 0, 1, - self.header.rbx.Size.Y.Offset - SEARCH_BOX_HEIGHT - PLACE_INFO_FRAME_HEIGHT),
|
|
}
|
|
|
|
self.placeInfoCardFrame.Parent = innerFrame
|
|
self.searchContainer.Parent = innerFrame
|
|
divider.Parent = innerFrame
|
|
self.conversationListFrame.Parent = innerFrame
|
|
|
|
self.rbx = Create.new"ImageButton" {
|
|
Active = true,
|
|
AutoButtonColor = false,
|
|
Size = UDim2.new(1, 0, 1, 0),
|
|
BackgroundColor3 = Constants.Color.GRAY5,
|
|
BorderSizePixel = 0,
|
|
|
|
Create.new("UIListLayout") {
|
|
Name = "ListLayout",
|
|
SortOrder = "LayoutOrder",
|
|
HorizontalAlignment = "Center",
|
|
},
|
|
|
|
self.header.rbx,
|
|
innerFrame,
|
|
}
|
|
|
|
if not appState.store:getState().ChatAppReducer.AppLoaded then
|
|
spawn(function()
|
|
appState.store:dispatch(
|
|
ConversationActions.GetLocalUserConversationsAsync(1, Constants.PageSize.GET_CONVERSATIONS)
|
|
):andThen(function()
|
|
appState.store:dispatch(SetAppLoaded(true))
|
|
end)
|
|
end)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
function GameShareComponent:Start()
|
|
local isLuaAppStarterScriptEnabled = FlagSettings:IsLuaAppStarterScriptEnabled()
|
|
|
|
self.connections = {}
|
|
|
|
-- back button
|
|
local backButtonConnection
|
|
if isLuaAppStarterScriptEnabled then
|
|
backButtonConnection = self.header.BackButtonPressed:connect(function()
|
|
self.appState.store:dispatch(NavigateBack())
|
|
GuiService:BroadcastNotification("", NotificationType.CLOSE_MODAL)
|
|
end)
|
|
else
|
|
backButtonConnection = self.header.BackButtonPressed:connect(function()
|
|
self.appState.store:dispatch(RemoveRoute(DialogInfo.Intent.GameShare))
|
|
GuiService:BroadcastNotification("", NotificationType.CLOSE_MODAL)
|
|
end)
|
|
end
|
|
table.insert(self.connections, backButtonConnection)
|
|
|
|
-- update
|
|
local appStateConnection = self.appState.store.changed:connect(function(state, oldState)
|
|
self:Update(state, oldState)
|
|
end)
|
|
table.insert(self.connections, appStateConnection)
|
|
|
|
-- search clear button
|
|
local clearButtonConnection = getInputEvent(self.clearSearchButton):Connect(function()
|
|
self.searchBox.Text = ""
|
|
end)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, clearButtonConnection)
|
|
else
|
|
table.insert(self.connections, clearButtonConnection)
|
|
end
|
|
|
|
local function updateClearButtonVisibility()
|
|
-- If we were to set the visible property of the clear button on the textbox focus lost event
|
|
-- it would disable the clear button, which in turn would stop the click event
|
|
-- from being able to notify the button
|
|
local visible = self.searchBox:IsFocused() and (self.searchBox.Text ~= "")
|
|
self.clearSearchButton.ImageTransparency = visible and 0 or 1
|
|
end
|
|
|
|
-- search box
|
|
local searchChangedConnection = self.searchBox:GetPropertyChangedSignal("Text"):Connect(function()
|
|
if self.list then
|
|
updateClearButtonVisibility()
|
|
self.list:SetFilterPredicate(self.SearchFilterPredicate)
|
|
self:getOlderConversationsForSearchIfNecessary()
|
|
end
|
|
end)
|
|
table.insert(self.connections, searchChangedConnection)
|
|
|
|
local focusedConnection = self.searchBox.Focused:Connect(updateClearButtonVisibility)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, focusedConnection)
|
|
else
|
|
table.insert(self.connections, focusedConnection)
|
|
end
|
|
local focusLostConnection = self.searchBox.FocusLost:Connect(updateClearButtonVisibility)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, focusLostConnection)
|
|
else
|
|
table.insert(self.connections, focusLostConnection)
|
|
end
|
|
|
|
if not self.placeInfo then
|
|
self.appState.store:dispatch(GetMultiplePlaceInfos({self.placeId}))
|
|
end
|
|
|
|
if self.appState.store:getState().ChatAppReducer.AppLoaded and self.placeInfo then
|
|
self:FillContent()
|
|
end
|
|
end
|
|
|
|
function GameShareComponent:Update(newState, oldState)
|
|
if (not oldState.ChatAppReducer.AppLoaded) and newState.ChatAppReducer.AppLoaded and self.placeInfo then
|
|
self:FillContent()
|
|
end
|
|
|
|
if (not self.placeInfo) and (newState.ChatAppReducer.PlaceInfos[self.placeId]) then
|
|
self.placeInfo = newState.ChatAppReducer.PlaceInfos[self.placeId]
|
|
if newState.ChatAppReducer.AppLoaded then
|
|
self:FillContent()
|
|
end
|
|
end
|
|
|
|
local newPageConversationsIsFetching = newState.ChatAppReducer.ConversationsAsync.pageConversationsIsFetching
|
|
local oldPageConversationsIsFetching = oldState.ChatAppReducer.ConversationsAsync.pageConversationsIsFetching
|
|
if newPageConversationsIsFetching ~= oldPageConversationsIsFetching and self.list then
|
|
self.list:Update(newState, oldState)
|
|
self:getOlderConversationsForSearchIfNecessary()
|
|
end
|
|
|
|
if newState.ChatAppReducer.Conversations ~= oldState.ChatAppReducer.Conversations and self.list then
|
|
self.list:Update(newState, oldState)
|
|
end
|
|
end
|
|
|
|
function GameShareComponent:FillContent()
|
|
self.searchContainer.Visible = true
|
|
self:FillPlaceInfo()
|
|
self:FillConversations()
|
|
end
|
|
|
|
function GameShareComponent:FillPlaceInfo()
|
|
if self.placeInfoCard then
|
|
return
|
|
end
|
|
|
|
self.placeInfoCard = PlaceInfoCard.new(self.appState, self.placeInfo)
|
|
self.placeInfoCard.rbx.Parent = self.placeInfoCardFrame
|
|
end
|
|
|
|
function GameShareComponent:FillConversations()
|
|
local conversations = self.appState.store:getState().ChatAppReducer.Conversations
|
|
local list = ConversationList.new(self.appState, conversations, GameShareCard)
|
|
list:SetSortWithConversationEntry(true)
|
|
self.list = list
|
|
list.rbx.Size = UDim2.new(1, 0, 1, 0)
|
|
list.rbx.Parent = self.conversationListFrame
|
|
|
|
local tappedConnection = list.ConversationTapped:connect(function(convoId)
|
|
local messageSentLocalTime = tick()
|
|
|
|
if FFlagShareGameToChatStatusAnalytics then
|
|
self.appState.store:dispatch(
|
|
ConversationActions.SendMessage(
|
|
convoId,
|
|
truncateAssetLink(self.placeInfo.url),
|
|
messageSentLocalTime
|
|
)):andThen(function(webStatus)
|
|
self:ReportSendButtonTappedEvent(convoId, webStatus)
|
|
|
|
if webStatus == HttpStatus.MODERATED then
|
|
local toastModel = ToastModel.new(Constants.ToastIDs.MESSAGE_WAS_MODERATED, "Feature.Chat.Message.GameLinkWasModerated")
|
|
self.appState.store:dispatch(ShowToast(toastModel))
|
|
end
|
|
end)
|
|
else
|
|
self:ReportSendButtonTappedEvent(convoId)
|
|
self.appState.store:dispatch(
|
|
ConversationActions.SendMessage(
|
|
convoId,
|
|
truncateAssetLink(self.placeInfo.url),
|
|
"Feature.Chat.Message.GameLinkWasModerated",
|
|
messageSentLocalTime
|
|
)
|
|
)
|
|
end
|
|
end)
|
|
table.insert(self.connections, tappedConnection)
|
|
|
|
local requestOlderConversationConnection = list.RequestOlderConversations:connect(function()
|
|
requestOlderConversations(self.appState)
|
|
end)
|
|
table.insert(self.connections, requestOlderConversationConnection)
|
|
end
|
|
|
|
function GameShareComponent:getOlderConversationsForSearchIfNecessary(appState)
|
|
-- To Check:
|
|
-- 1) Search is open
|
|
-- 2) Not have loaded all conversations.
|
|
-- 3) Not Ccrrently getting older conversations
|
|
-- 4) Having enouth search items to show
|
|
-- Note that we already try to load more conversations if we scroll down to the bottom of the list
|
|
local state = self.appState.store:getState()
|
|
local isSearchOpen = (self.searchBox.Text) ~= nil and (self.searchBox.Text ~= "")
|
|
if (not isSearchOpen) or state.ChatAppReducer.ConversationsAsync.oldestConversationIsFetched
|
|
or state.ChatAppReducer.ConversationsAsync.pageConversationsIsFetching then
|
|
return
|
|
end
|
|
|
|
if self.list.rbx.CanvasSize.Y.Offset > self.list.rbx.AbsoluteSize.Y then
|
|
return
|
|
end
|
|
|
|
requestOlderConversations(self.appState)
|
|
end
|
|
|
|
function GameShareComponent:ReportSendButtonTappedEvent(convoId, httpStatus)
|
|
local eventName = "clickSendBtnFromGameShareCard"
|
|
local eventContext = "touch"
|
|
|
|
local player = PlayerService.LocalPlayer
|
|
local userId = "UNKNOWN"
|
|
if player then
|
|
userId = tostring(player.UserId)
|
|
end
|
|
|
|
local additionalArgs = {
|
|
uid = userId,
|
|
placeid = self.placeId,
|
|
cid = convoId,
|
|
httpStatus = httpStatus,
|
|
}
|
|
|
|
self._analytics.EventStream:setRBXEvent(eventContext, eventName, additionalArgs)
|
|
end
|
|
|
|
function GameShareComponent:Stop()
|
|
for _, connection in ipairs(self.connections) do
|
|
connection:disconnect()
|
|
end
|
|
self.connections = {}
|
|
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
for _, connection in ipairs(self.rbx_connections) do
|
|
connection:Disconnect()
|
|
end
|
|
self.rbx_connections = {}
|
|
end
|
|
end
|
|
|
|
function GameShareComponent:Destruct()
|
|
if self.list then
|
|
self.list:Destruct()
|
|
end
|
|
if self.header then
|
|
self.header:Destroy()
|
|
end
|
|
if self.placeInfoCard then
|
|
self.placeInfoCard:Destruct()
|
|
end
|
|
self.rbx:Destroy()
|
|
end
|
|
|
|
return GameShareComponent |