360 lines
11 KiB
Lua
360 lines
11 KiB
Lua
local CoreGui = game:GetService("CoreGui")
|
|
|
|
local Modules = CoreGui.RobloxGui.Modules
|
|
local Common = Modules.Common
|
|
local LuaChat = Modules.LuaChat
|
|
local LuaApp = Modules.LuaApp
|
|
|
|
local Constants = require(LuaChat.Constants)
|
|
local Create = require(LuaChat.Create)
|
|
local Functional = require(Common.Functional)
|
|
local Signal = require(Common.Signal)
|
|
local ToastModel = require(LuaChat.Models.ToastModel)
|
|
local getInputEvent = require(LuaChat.Utils.getInputEvent)
|
|
|
|
local Components = LuaChat.Components
|
|
local ListSection = require(Components.ListSection)
|
|
local UserList = require(Components.UserList)
|
|
local UserThumbnailBar = require(Components.UserThumbnailBar)
|
|
|
|
local User = require(LuaApp.Models.User)
|
|
|
|
local ShowToast = require(LuaChat.Actions.ShowToast)
|
|
|
|
local FFlagLuaChatToSplitRbxConnections = settings():GetFFlag("LuaChatToSplitRbxConnections")
|
|
local FFlagTextBoxOverrideManualFocusRelease = settings():GetFFlag("TextBoxOverrideManualFocusRelease")
|
|
local FFlagLuaChatSortFriendsForConversation = settings():GetFFlag("LuaChatSortFriendsForConversation")
|
|
|
|
local CLEAR_TEXT_WIDTH = 44
|
|
local ICON_CELL_WIDTH = 60
|
|
local SEARCH_BOX_HEIGHT = 48
|
|
|
|
local FriendSearchBox = {}
|
|
FriendSearchBox.__index = FriendSearchBox
|
|
|
|
local PRESENCE_WEIGHTS = {
|
|
[User.PresenceType.IN_GAME] = 4,
|
|
[User.PresenceType.ONLINE] = 3,
|
|
[User.PresenceType.IN_STUDIO] = 2,
|
|
[User.PresenceType.OFFLINE] = 1,
|
|
}
|
|
|
|
local function friendSortPredicate(friend1, friend2)
|
|
local friend1Weight = PRESENCE_WEIGHTS[friend1.presence] or 0
|
|
local friend2Weight = PRESENCE_WEIGHTS[friend2.presence] or 0
|
|
|
|
if friend1Weight == friend2Weight then
|
|
return friend1.name:lower() < friend2.name:lower()
|
|
else
|
|
return friend1Weight > friend2Weight
|
|
end
|
|
end
|
|
|
|
function FriendSearchBox.new(appState, participants, maxParticipantCount, filter)
|
|
local self = {
|
|
appState = appState,
|
|
participants = participants,
|
|
users = appState.store:getState().Users,
|
|
maxParticipantCount = maxParticipantCount,
|
|
}
|
|
self.connections = {}
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
self.rbx_connections = {}
|
|
end
|
|
setmetatable(self, FriendSearchBox)
|
|
|
|
self.friendThumbnails = UserThumbnailBar.new(appState, maxParticipantCount, 1)
|
|
local removedConnection = self.friendThumbnails.removed:connect(function(id)
|
|
self:RemoveParticipant(id)
|
|
end)
|
|
table.insert(self.connections, removedConnection)
|
|
|
|
self.userList = UserList.new(appState, nil, filter)
|
|
local userSelectedConnection = self.userList.userSelected:connect(function(user)
|
|
local selected = Functional.Find(self.participants, user.id)
|
|
if selected then
|
|
self:RemoveParticipant(user.id)
|
|
else
|
|
self:AddParticipant(user.id)
|
|
end
|
|
self:ClearText()
|
|
end)
|
|
table.insert(self.connections, userSelectedConnection)
|
|
|
|
self.rbx = Create.new"Frame" {
|
|
Name = "FriendSearchBox",
|
|
BackgroundTransparency = 1,
|
|
BorderSizePixel = 0,
|
|
Size = UDim2.new(1, 0, 1, 0),
|
|
|
|
Create.new"UIListLayout" {
|
|
Name = "ListLayout",
|
|
SortOrder = "LayoutOrder",
|
|
},
|
|
self.friendThumbnails.rbx,
|
|
Create.new"Frame" {
|
|
Name = "Divider1",
|
|
BackgroundColor3 = Constants.Color.GRAY4,
|
|
BorderSizePixel = 0,
|
|
Size = UDim2.new(1, 0, 0, 1),
|
|
LayoutOrder = 2,
|
|
},
|
|
ListSection.new(appState, nil, 3).rbx,
|
|
Create.new"Frame" {
|
|
Name = "SearchContainer",
|
|
BackgroundTransparency = 0,
|
|
BackgroundColor3 = Constants.Color.WHITE,
|
|
BorderSizePixel = 0,
|
|
Size = UDim2.new(1, 0, 0, SEARCH_BOX_HEIGHT),
|
|
LayoutOrder = 4,
|
|
|
|
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.Friends.Label.SearchFriends"),
|
|
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,
|
|
},
|
|
},
|
|
Create.new"Frame" {
|
|
Name = "Divider2",
|
|
BackgroundColor3 = Constants.Color.GRAY4,
|
|
BorderSizePixel = 0,
|
|
Size = UDim2.new(1, 0, 0, 1),
|
|
LayoutOrder = 5,
|
|
},
|
|
Create.new"ScrollingFrame" {
|
|
Name = "ScrollingFrame",
|
|
Size = UDim2.new(1, 0, 1, 0),
|
|
BackgroundTransparency = 1,
|
|
BorderSizePixel = 0,
|
|
ScrollBarThickness = 5,
|
|
BottomImage = "rbxasset://textures/ui/LuaChat/9-slice/scroll-bar.png",
|
|
MidImage = "rbxasset://textures/ui/LuaChat/9-slice/scroll-bar.png",
|
|
TopImage = "rbxasset://textures/ui/LuaChat/9-slice/scroll-bar.png",
|
|
LayoutOrder = 6,
|
|
|
|
self.userList.rbx,
|
|
},
|
|
}
|
|
|
|
self.searchContainer = self.rbx.SearchContainer
|
|
local clearButton = self.searchContainer.Clear
|
|
local search = self.searchContainer.Search
|
|
self.search = search
|
|
if FFlagTextBoxOverrideManualFocusRelease then
|
|
search.ManualFocusRelease = true
|
|
end
|
|
|
|
local clearButtonConnection = getInputEvent(clearButton):Connect(function()
|
|
self:ClearText()
|
|
end)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, clearButtonConnection)
|
|
else
|
|
table.insert(self.connections, clearButtonConnection)
|
|
end
|
|
|
|
self:Resize()
|
|
|
|
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 = search:IsFocused() and (search.Text ~= "")
|
|
clearButton.ImageTransparency = visible and 0 or 1
|
|
end
|
|
|
|
self.searchChanged = Signal.new()
|
|
self.addParticipant = Signal.new()
|
|
self.removeParticipant = Signal.new()
|
|
|
|
local searchChangedConnection = search:GetPropertyChangedSignal("Text"):Connect(function()
|
|
updateClearButtonVisibility()
|
|
self.userList:ApplySearch(search.Text)
|
|
self:Resize()
|
|
self:ResizeCanvas()
|
|
end)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, searchChangedConnection)
|
|
else
|
|
table.insert(self.connections, searchChangedConnection)
|
|
end
|
|
|
|
local focusedConnection = search.Focused:Connect(updateClearButtonVisibility)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, focusedConnection)
|
|
else
|
|
table.insert(self.connections, focusedConnection)
|
|
end
|
|
local focusLostConnection = search.FocusLost:Connect(updateClearButtonVisibility)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, focusLostConnection)
|
|
else
|
|
table.insert(self.connections, focusLostConnection)
|
|
end
|
|
|
|
self:UpdateFriends(appState.store:getState().Users, self.participants)
|
|
|
|
local userListAddConnection = self.userList.rbx.ChildAdded:Connect(function(child)
|
|
self:ResizeCanvas();
|
|
end)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, userListAddConnection)
|
|
else
|
|
table.insert(self.connections, userListAddConnection)
|
|
end
|
|
|
|
local userListRemoveConnection = self.userList.rbx.ChildRemoved:Connect(function(child)
|
|
self:ResizeCanvas();
|
|
end)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, userListRemoveConnection)
|
|
else
|
|
table.insert(self.connections, userListRemoveConnection)
|
|
end
|
|
|
|
local userListSizeConnection = self.userList.rbx:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
|
|
self:ResizeCanvas()
|
|
end)
|
|
if FFlagLuaChatToSplitRbxConnections then
|
|
table.insert(self.rbx_connections, userListSizeConnection)
|
|
else
|
|
table.insert(self.connections, userListSizeConnection)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
function FriendSearchBox:Resize()
|
|
local height = 0
|
|
for _, element in pairs(self.rbx:GetChildren()) do
|
|
if element:IsA("GuiObject") and element.Visible and element.Name ~= "ScrollingFrame" then
|
|
height = height + element.AbsoluteSize.Y
|
|
end
|
|
end
|
|
|
|
self.rbx.ScrollingFrame.Size = UDim2.new(1, 0, 1, -height)
|
|
end
|
|
|
|
function FriendSearchBox:ResizeCanvas()
|
|
local height = 0
|
|
for _, element in pairs(self.userList.rbx:GetChildren()) do
|
|
if element:IsA("GuiObject") and element.Visible then
|
|
height = height + element.AbsoluteSize.Y
|
|
end
|
|
end
|
|
self.rbx.ScrollingFrame.CanvasSize = UDim2.new(1, 0, 0, height)
|
|
end
|
|
|
|
function FriendSearchBox:AddParticipant(userId)
|
|
if #self.participants >= self.maxParticipantCount then
|
|
|
|
if self.tooManyFriendsToastModel == nil then
|
|
local messageKey = "Feature.Chat.Message.ToastText"
|
|
local messageArguments = {
|
|
friendNum = tostring(Constants.MAX_PARTICIPANT_COUNT),
|
|
}
|
|
local toastModel = ToastModel.new(Constants.ToastIDs.TOO_MANY_PEOPLE, messageKey, messageArguments)
|
|
self.tooManyFriendsToastModel = toastModel
|
|
end
|
|
|
|
self.appState.store:dispatch(ShowToast(self.tooManyFriendsToastModel))
|
|
else
|
|
self.addParticipant:fire(userId)
|
|
end
|
|
end
|
|
|
|
function FriendSearchBox:RemoveParticipant(userId)
|
|
self.removeParticipant:fire(userId)
|
|
end
|
|
|
|
function FriendSearchBox:UpdateFriends(users, selectedList)
|
|
local friends = {}
|
|
for _, user in pairs(users) do
|
|
table.insert(friends, user)
|
|
end
|
|
if FFlagLuaChatSortFriendsForConversation then
|
|
table.sort(friends, friendSortPredicate)
|
|
for _,friend in pairs (friends) do
|
|
if not PRESENCE_WEIGHTS[friend.presence] then
|
|
warn("Invalid presence value for "..friend.name)
|
|
end
|
|
end
|
|
end
|
|
self.userList:Update(friends, selectedList)
|
|
self:Resize()
|
|
end
|
|
|
|
function FriendSearchBox:ClearText()
|
|
self.searchContainer.Search.Text = ""
|
|
self:Resize()
|
|
end
|
|
|
|
function FriendSearchBox:Update(participants)
|
|
local state = self.appState.store:getState()
|
|
local users = state.Users
|
|
|
|
if participants ~= self.participants then
|
|
self.friendThumbnails:Update(participants)
|
|
end
|
|
|
|
if participants ~= self.participants or users ~= self.users then
|
|
self:UpdateFriends(users, participants)
|
|
end
|
|
|
|
self.participants = participants
|
|
self.users = users
|
|
|
|
self:Resize()
|
|
end
|
|
|
|
function FriendSearchBox: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
|
|
|
|
self.userList:Destruct()
|
|
self.friendThumbnails:Destruct()
|
|
self.rbx.Parent = nil
|
|
self.rbx:Destroy()
|
|
end
|
|
|
|
return FriendSearchBox
|