SyntaxGameServer/RCCService2018/content/internal/Chat/Modules/LuaChat/Components/ChatBubble.lua

323 lines
8.7 KiB
Lua

local Players = game:GetService("Players")
local GuiService = game:GetService("GuiService")
local LuaChat = script.Parent.Parent
local UserThumbnail = require(script.Parent.UserThumbnail)
local TypingIndicator = require(script.Parent.TypingIndicator)
local UserChatBubble = require(script.Parent.UserChatBubble)
local AssetCard = require(script.Parent.AssetCard)
local Create = require(LuaChat.Create)
local Constants = require(LuaChat.Constants)
local WebApi = require(LuaChat.WebApi)
local Modules = game:GetService("CoreGui").RobloxGui.Modules
local LuaApp = Modules.LuaApp
local NotificationType = require(LuaApp.Enum.NotificationType)
local FFlagLuaChatToSplitRbxConnections = settings():GetFFlag("LuaChatToSplitRbxConnections")
local RECEIVED_BUBBLE = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble2.png"
local RECEIVED_BUBBLE_WITH_TAIL = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble.png"
local RECEIVED_TAIL = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble-tip.png"
local SENT_BUBBLE = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble-self2.png"
local SENT_BUBBLE_WITH_TAIL = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble-self.png"
local SENT_TAIL = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble-self-tip.png"
local SENT_BUBBLE_OUTLINE = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble2.png"
local SENT_BUBBLE_OUTLINE_WITH_TAIL = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble-right.png"
local SENT_OUTLINE_TAIL = "rbxasset://textures/ui/LuaChat/9-slice/chat-bubble-tip-right.png"
local FFlagLuaChatInfiniteRelayoutRecursionFix = settings():GetFFlag("LuaChatInfiniteRelayoutRecursionFix")
local function isOutgoingMessage(message)
local localUserId = tostring(Players.LocalPlayer.UserId)
return message.senderTargetId == localUserId
end
local function isMessageSending(conversation, message)
if conversation and conversation.sendingMessages then
return conversation.sendingMessages:Get(message.id) ~= nil
end
return false
end
local PROTOCOL_IDENTIFIERS = {
"https?://", ""
}
local RESOURCE_NAMES = {
"www%.", "web%.", ""
}
local WHITELISTED_DOMAINS = {
"roblox", "sitetest%d%.robloxlabs", "gametest%d%.robloxlabs"
}
local MESSAGE_CONTENT_PATTERNS = {
GAME_LINK = "%.com/games[^%d]*(%d+)/?",
}
local ChatBubble = {}
ChatBubble.__index = ChatBubble
ChatBubble.BubbleType = {
AssetCard = "AssetCard",
ChatBubble = "UserChatBubble",
}
local function getBubbleImages(message, bubbleType)
if isOutgoingMessage(message) and bubbleType ~= ChatBubble.BubbleType.AssetCard then
return SENT_BUBBLE, SENT_BUBBLE_WITH_TAIL, SENT_TAIL
elseif isOutgoingMessage(message) then
return SENT_BUBBLE_OUTLINE, SENT_BUBBLE_OUTLINE_WITH_TAIL, SENT_OUTLINE_TAIL
else
return RECEIVED_BUBBLE, RECEIVED_BUBBLE_WITH_TAIL, RECEIVED_TAIL
end
end
function ChatBubble.new(appState, message, width)
width = width or 0
local self = {}
setmetatable(self, ChatBubble)
local conversationId = message.conversationId
local isSending = isMessageSending(appState.store:getState().ChatAppReducer.Conversations[conversationId], message)
if FFlagLuaChatInfiniteRelayoutRecursionFix then
self.width = width
end
self.appState = appState
self.message = message
self.bubbles = {}
self.connections = {}
if FFlagLuaChatToSplitRbxConnections then
self.rbx_connections = {}
end
self.tailVisible = false
self.rbx = Create.new "Frame" {
Name = "ChatContainer",
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, 0),
Create.new "UIListLayout" {
SortOrder = Enum.SortOrder.LayoutOrder,
},
}
if message.moderated or isSending then
self:AddBubble(UserChatBubble.new(appState, message, nil, self.width), 1)
-- Specifically whitelist strings with .com/games in the url
elseif message.content:lower():match(MESSAGE_CONTENT_PATTERNS.GAME_LINK) then
local text = self:FilterForLinks()
-- Flush remaining text if it is not empty
if text:gsub("%s+","") ~= "" then
self:AddBubble(UserChatBubble.new(appState, message, text, self.width))
end
else
self:AddBubble(UserChatBubble.new(appState, message, nil, self.width), 1)
end
return self
end
function ChatBubble:FilterForLinks()
local text = self.message.content
for _, protocol in pairs(PROTOCOL_IDENTIFIERS) do
for _, resource in pairs(RESOURCE_NAMES) do
for _, domain in pairs(WHITELISTED_DOMAINS) do
local constructedUrlPattern = protocol .. resource .. domain .. MESSAGE_CONTENT_PATTERNS.GAME_LINK
for assetId in text:lower():gmatch(constructedUrlPattern) do
local linkStart, endLink = text:lower():find("[^%s*]*" .. constructedUrlPattern .. "[^%s*]*")
if linkStart then
local textBefore = text:sub(1, linkStart - 1)
if textBefore:gsub("%s+","") ~= "" then
self:AddBubble(UserChatBubble.new(self.appState, self.message, textBefore, self.width))
end
self:AddBubble(AssetCard.new(self.appState, self.message, assetId))
text = text:sub(endLink + 1)
else
return text
end
end
end
end
end
return text
end
function ChatBubble:AddBubble(bubble, placement)
table.insert(self.bubbles, placement or #self.bubbles+1 ,bubble)
bubble.rbx.Parent = self.rbx
bubble.LayoutOrder = placement or #self.bubbles
for i=1,#self.bubbles do
self.bubbles[i].LayoutOrder = i
end
local connection = bubble.rbx:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
self:Resize()
end)
if FFlagLuaChatToSplitRbxConnections then
table.insert(self.rbx_connections, connection)
else
table.insert(self.connections, connection)
end
self:Update()
end
function ChatBubble:SetUsernameVisible(value)
local bubblePos = self.bubbles[1].bubble.Position
if value then
self.bubbles[1].usernameLabel.Visible = true
self.bubbles[1].bubble.Position = UDim2.new(
bubblePos.X.Scale,
bubblePos.X.Offset,
0,
16
)
else
self.bubbles[1].usernameLabel.Visible = false
self.bubbles[1].bubble.Position = UDim2.new(
bubblePos.X.Scale,
bubblePos.X.Offset,
0,
0
)
end
self:Resize()
end
function ChatBubble:SetTypingIndicatorVisible(value)
if value and not self.indicator then
local indicator = TypingIndicator.new(self.appState, .4)
indicator.rbx.AnchorPoint = Vector2.new(0,0.5)
indicator.rbx.Position = UDim2.new(0, self.bubbles[1].usernameLabel.TextBounds.X + 3, 0.5, 0)
indicator.rbx.Parent = self.bubbles[1].usernameLabel
self.indicator = indicator
elseif self.indicator and not value then
self.indicator:Destroy()
self.indicator = nil
end
end
function ChatBubble:SetThumbnailVisible(value)
if value then
self.thumbnail = UserThumbnail.new(self.appState, self.message.senderTargetId, true)
self.thumbnail.rbx.Position = UDim2.new(0, 10, 0, 0)
self.thumbnail.rbx.Overlay.ImageColor3 = Constants.Color.GRAY6
self.thumbnail.rbx.Parent = self.bubbles[1].bubbleContainer
self.thumbnail.clicked:connect(function()
local user = self.appState.store:getState().Users[self.message.senderTargetId]
local userId = user and user.id
if userId then
GuiService:BroadcastNotification(WebApi.MakeUserProfileUrl(userId),
NotificationType.VIEW_PROFILE)
end
end)
else
if self.thumbnail then
self.thumbnail:Destruct()
end
end
end
function ChatBubble:SetTailVisible(value)
self.tailVisible = value
if not self.bubbles[1] then return end
for i, bubble in pairs(self.bubbles) do
local bubbleImage, bubbleWithTail, tailImage = getBubbleImages(self.message, bubble.bubbleType)
if value and i == 1 then
bubble.bubble.Image = bubbleWithTail
bubble.tail.Image = tailImage
bubble.tail.Visible = true
else
bubble.bubble.Image = bubbleImage
bubble.tail.Visible = false
end
end
end
function ChatBubble:SetPaddingObject(object)
if not self.bubbles[1] then return end
if self.bubbles[1].paddingObject then
self.bubbles[1].paddingObject:Destroy()
end
object.LayoutOrder = 1
object.Parent = self.bubbles[1].rbx
self.bubbles[1].paddingObject = object
self.bubbles[1]:Resize()
end
function ChatBubble:Resize()
if not FFlagLuaChatInfiniteRelayoutRecursionFix then
for _,bubble in pairs(self.bubbles) do
bubble:Resize()
end
end
local height = 0
for _, child in ipairs(self.rbx:GetChildren()) do
if child:IsA("GuiObject") then
height = height + child.AbsoluteSize.Y
end
end
self.rbx.Size = UDim2.new(1, 0, 0, height)
end
function ChatBubble:Update()
self:SetTailVisible(self.tailVisible)
self:Resize()
end
function ChatBubble:Destruct()
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
for _, bubble in ipairs(self.bubbles) do
bubble:Destruct()
end
if self.thumbnail then
self.thumbnail:Destruct()
end
self.thumbnail = nil
self.rbx:Destroy()
end
return ChatBubble