add gs
This commit is contained in:
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
--TODO: Currently Under Migration to CorePackages
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
--
|
||||
return Action("GetRecentlyPlayedGames", function()
|
||||
return {}
|
||||
end)
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
return function()
|
||||
describe("require", function()
|
||||
it("should create without errors", function()
|
||||
require(script.Parent.GetRecentlyPlayedGames)
|
||||
end)
|
||||
|
||||
it("should set the name", function()
|
||||
local action = require(script.Parent.GetRecentlyPlayedGames)
|
||||
|
||||
expect(action.name).to.equal("GetRecentlyPlayedGames")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("call", function()
|
||||
it("should return a table when called as a function", function()
|
||||
local action = require(script.Parent.GetRecentlyPlayedGames)
|
||||
|
||||
action = action({})
|
||||
expect(action).to.be.a("table")
|
||||
end)
|
||||
|
||||
it("should set the type", function()
|
||||
local action = require(script.Parent.GetRecentlyPlayedGames)
|
||||
|
||||
action = action({})
|
||||
expect(action.type).to.equal("GetRecentlyPlayedGames")
|
||||
end)
|
||||
|
||||
it("should set the type and name to be equal", function()
|
||||
local action = require(script.Parent.GetRecentlyPlayedGames)
|
||||
|
||||
local actionItem = action({})
|
||||
expect(actionItem.type).to.equal(action.name)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
--TODO: Currently Under Migration to CorePackages
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
--
|
||||
|
||||
return Action("SetRecentlyPlayedGames", function(gameSortData)
|
||||
return {
|
||||
gameSortData = gameSortData,
|
||||
}
|
||||
end)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
return function()
|
||||
describe("require", function()
|
||||
it("should create without errors", function()
|
||||
require(script.Parent.SetRecentlyPlayedGames)
|
||||
end)
|
||||
|
||||
it("should set the name", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGames)
|
||||
|
||||
expect(action.name).to.equal("SetRecentlyPlayedGames")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("call", function()
|
||||
it("should return a table when called as a function", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGames)
|
||||
|
||||
action = action({})
|
||||
expect(action).to.be.a("table")
|
||||
end)
|
||||
|
||||
it("should set the type", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGames)
|
||||
|
||||
action = action({})
|
||||
expect(action.type).to.equal("SetRecentlyPlayedGames")
|
||||
end)
|
||||
|
||||
it("should set the games", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGames)
|
||||
|
||||
local gameSortData = {}
|
||||
action = action(gameSortData)
|
||||
expect(action.gameSortData).to.be.a("table")
|
||||
end)
|
||||
|
||||
it("should set the type and name to be equal", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGames)
|
||||
|
||||
local actionItem = action({})
|
||||
expect(actionItem.type).to.equal(action.name)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
--TODO: Currently Under Migration to CorePackages
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
--
|
||||
return Action("SetRecentlyPlayedGamesFetching", function(fetching)
|
||||
return {
|
||||
fetching = fetching,
|
||||
}
|
||||
end)
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
return function()
|
||||
describe("require", function()
|
||||
it("should create without errors", function()
|
||||
require(script.Parent.SetRecentlyPlayedGamesFetching)
|
||||
end)
|
||||
|
||||
it("should set the name", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGamesFetching)
|
||||
|
||||
expect(action.name).to.equal("SetRecentlyPlayedGamesFetching")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("call", function()
|
||||
it("should return a table when called as a function", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGamesFetching)
|
||||
|
||||
action = action({})
|
||||
expect(action).to.be.a("table")
|
||||
end)
|
||||
|
||||
it("should set the type", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGamesFetching)
|
||||
|
||||
action = action({})
|
||||
expect(action.type).to.equal("SetRecentlyPlayedGamesFetching")
|
||||
end)
|
||||
|
||||
it("should set fetching status.", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGamesFetching)
|
||||
|
||||
action = action(true)
|
||||
expect(action.fetching).to.equal(true)
|
||||
end)
|
||||
|
||||
it("should set the type and name to be equal", function()
|
||||
local action = require(script.Parent.SetRecentlyPlayedGamesFetching)
|
||||
|
||||
local actionItem = action({})
|
||||
expect(actionItem.type).to.equal(action.name)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--[[
|
||||
Returns a blank selector image.
|
||||
]]
|
||||
return function()
|
||||
local BlankSelectionImage = Instance.new("ImageLabel")
|
||||
BlankSelectionImage.Name = "BlankSelectorImage"
|
||||
BlankSelectionImage.Position = UDim2.new(0, 0, 0, 0)
|
||||
BlankSelectionImage.Size = UDim2.new(1, 0, 1, 0)
|
||||
BlankSelectionImage.BackgroundTransparency = 1
|
||||
return BlankSelectionImage
|
||||
end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
--[[
|
||||
Returns a selection image with rounded corners.
|
||||
]]
|
||||
local Settings = script.Parent.Parent
|
||||
local Constants = require(Settings.Pages.LeaveGameScreen.Constants)
|
||||
|
||||
return function()
|
||||
local RoundedSelectionImage = Instance.new("ImageLabel")
|
||||
RoundedSelectionImage.Name = "SelectorImage"
|
||||
RoundedSelectionImage.Image = Constants.Image.BUTTON_SELECTOR
|
||||
RoundedSelectionImage.Position = UDim2.new(0, -7, 0, -7)
|
||||
RoundedSelectionImage.Size = UDim2.new(1, 14, 1, 14)
|
||||
RoundedSelectionImage.BackgroundTransparency = 1
|
||||
RoundedSelectionImage.ScaleType = Enum.ScaleType.Slice
|
||||
RoundedSelectionImage.SliceCenter = Rect.new(31, 31, 63, 63)
|
||||
return RoundedSelectionImage
|
||||
end
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
--[[
|
||||
Creates a Roact component that is a blue rounded button
|
||||
Props:
|
||||
position : UDim2 - Position of the button.
|
||||
zIndex : number - Determines the order of UI element rendering.
|
||||
anchorPoint : UDim2 - The anchor point of the button.
|
||||
objRef : Ref - The ref of this button.
|
||||
]]
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local RunService = game:GetService("RunService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Settings = script.Parent.Parent
|
||||
local Modules = Settings.Parent
|
||||
local Common = Modules.Common
|
||||
local Analytics = require(Common.Analytics)
|
||||
local Constants = require(Settings.Pages.LeaveGameScreen.Constants)
|
||||
local RoundedSelectionImage = require(Settings.Common.RoundedSelectionImage)
|
||||
local RobloxTranslator = require(Modules.RobloxTranslator)
|
||||
|
||||
local LeaveButton = Roact.PureComponent:extend("LeaveButton")
|
||||
|
||||
local LEAVE_GAME_FRAME_WAITS = 2
|
||||
local LEAVE_GAME_BUTTON_LABEL = "Feature.SettingsHub.Label.LeaveButton"
|
||||
|
||||
-- NOTE: event context might need to be changed for other platforms.
|
||||
local EVENT_CONTEXT = "XboxOne"
|
||||
function LeaveButton:init()
|
||||
self.analytics = Analytics.new()
|
||||
self.onSelectionGained = function()
|
||||
self:setState({
|
||||
focused = true,
|
||||
})
|
||||
end
|
||||
self.onSelectionLost = function()
|
||||
self:setState({
|
||||
focused = false,
|
||||
})
|
||||
end
|
||||
self.leaveFunc = function()
|
||||
GuiService.SelectedCoreObject = nil -- deselects the button and prevents spamming the popup to save in studio when using gamepad
|
||||
|
||||
-- need to wait for render frames so on slower devices the leave button highlight will update
|
||||
-- otherwise, since on slow devices it takes so long to leave you are left wondering if you pressed the button
|
||||
for i = 1, LEAVE_GAME_FRAME_WAITS do
|
||||
RunService.RenderStepped:wait()
|
||||
end
|
||||
local eventName = "LeaveGame"
|
||||
self.analytics.EventStream:setRBXEventStream(EVENT_CONTEXT, eventName)
|
||||
game:Shutdown()
|
||||
end
|
||||
end
|
||||
|
||||
function LeaveButton:render()
|
||||
local size = UDim2.new(0, 320, 0, 80)
|
||||
local position = self.props.position or UDim2.new(0.5, 0, 0.5, 0)
|
||||
local anchorPoint = self.props.anchorPoint or Vector2.new(0.5, 0.5)
|
||||
local zIndex = self.props.zIndex
|
||||
local text = RobloxTranslator:FormatByKey(LEAVE_GAME_BUTTON_LABEL)
|
||||
local textColor = Constants.Color.WHITE
|
||||
local selector = RoundedSelectionImage()
|
||||
if self.state.focused then
|
||||
textColor = Constants.Color.DARK
|
||||
end
|
||||
local label = Roact.createElement("TextLabel", {
|
||||
Text = text,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Font = Enum.Font.SourceSans,
|
||||
TextSize = Constants.TextSize.BUTTON,
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
TextColor3 = textColor,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex + 1,
|
||||
})
|
||||
return Roact.createElement("ImageButton", {
|
||||
Image = Constants.Image.BUTTON,
|
||||
Size = size,
|
||||
Position = position,
|
||||
AnchorPoint = anchorPoint,
|
||||
[Roact.Ref] = self.props.objRef,
|
||||
[Roact.Event.SelectionGained] = self.onSelectionGained,
|
||||
[Roact.Event.SelectionLost] = self.onSelectionLost,
|
||||
[Roact.Event.Activated] = self.leaveFunc,
|
||||
ImageColor3 = Constants.Color.BLUE,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(8, 8, 9, 9),
|
||||
SelectionImageObject = selector,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex,
|
||||
},{
|
||||
Label = label,
|
||||
})
|
||||
end
|
||||
return LeaveButton
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
--[[
|
||||
A Roact play next game icon. When activated, the player will join that game.
|
||||
Props:
|
||||
universeId : number - The universe id of the game.
|
||||
placeId: number - The place id of the game.
|
||||
imageToken: string - The image token of the game thumbnail.
|
||||
size : UDim2 - The size of the game icon.
|
||||
position : position - The position of the game icon.
|
||||
layoutOrder : number - The sort order of the game icon.
|
||||
zIndex : number - Determines the order of UI element rendering.
|
||||
focused : bool - Determines if the game icon focused.
|
||||
onSelectionGained : function() - The callback function for selection gained.
|
||||
objRef : Ref - The ref of the game icon.
|
||||
]]
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Otter = require(CorePackages.Otter)
|
||||
|
||||
local Settings = script.Parent.Parent.Parent
|
||||
local Modules = Settings.Parent
|
||||
local Common = Modules.Common
|
||||
local Analytics = require(Common.Analytics)
|
||||
local BlankSelectionImage = require(Settings.Common.BlankSelectionImage)
|
||||
local Utility = require(Settings.Utility)
|
||||
|
||||
local GameIcon = Roact.PureComponent:extend("GameIcon")
|
||||
|
||||
local NORMAL_SIZE = 200
|
||||
local FOCUSED_SIZE = 232
|
||||
|
||||
-- NOTE: event context might need to be changed for other platforms.
|
||||
local EVENT_CONTEXT = "XboxOne"
|
||||
function GameIcon:init()
|
||||
self.ref = Roact.createRef()
|
||||
self.analytics = Analytics.new()
|
||||
self.motorOptions = {
|
||||
dampingRatio = 1,
|
||||
frequency = 5,
|
||||
}
|
||||
self.PlayNextGame = function()
|
||||
if PlatformService then
|
||||
local eventName = "PlayNextGame"
|
||||
local args = {
|
||||
UniverseID = self.props.universeId,
|
||||
PlaceId = self.props.placeId
|
||||
}
|
||||
self.analytics.EventStream:setRBXEventStream(EVENT_CONTEXT, eventName, args)
|
||||
PlatformService:PlayNextGame(self.props.placeId)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function GameIcon:render()
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local size = UDim2.new(0, NORMAL_SIZE, 0, NORMAL_SIZE)
|
||||
local position = self.props.position
|
||||
local image = string.format("https://games.roblox.com/v1/games/game-thumbnail?imageToken=%s&height=250&width=250", self.props.imageToken)
|
||||
local zIndex = self.props.zIndex
|
||||
local selector = BlankSelectionImage()
|
||||
return Roact.createElement("ImageButton",
|
||||
{
|
||||
Size = size,
|
||||
Position = position,
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Image = image,
|
||||
BackgroundColor3 = Color3.fromRGB(0,0,0),
|
||||
BackgroundTransparency = 0.8,
|
||||
BorderSizePixel = 0,
|
||||
LayoutOrder = layoutOrder,
|
||||
SelectionImageObject = selector,
|
||||
[Roact.Event.SelectionGained] = self.props.onSelectionGained,
|
||||
[Roact.Event.Activated] = function()
|
||||
self.PlayNextGame(self.props.placeId)
|
||||
end,
|
||||
[Roact.Ref] = self.ref,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
end
|
||||
|
||||
function GameIcon:didMount()
|
||||
self.motor = Otter.createSingleMotor(NORMAL_SIZE)
|
||||
self.motor:onStep(function(value)
|
||||
local t = value/100
|
||||
if self.ref.current ~= nil then
|
||||
local size = Utility:Lerp(t, FOCUSED_SIZE, NORMAL_SIZE)
|
||||
size = Utility:Round(size)
|
||||
self.ref.current.Size = UDim2.new(0, size, 0, size)
|
||||
end
|
||||
end)
|
||||
self.motor:start()
|
||||
self.motor:setGoal(Otter.instant(100))
|
||||
end
|
||||
|
||||
function GameIcon:didUpdate(previousProps, previousState)
|
||||
if self.props.focused == previousProps.focused then
|
||||
return
|
||||
end
|
||||
local targetPercentage = self.props.focused and 0 or 100
|
||||
self.motor:setGoal(Otter.spring(targetPercentage, self.motorOptions))
|
||||
end
|
||||
|
||||
return GameIcon
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
A Roact play next game selector.
|
||||
Props:
|
||||
position : UDim2 - Position of the button.
|
||||
anchorPoint : UDim2 - The anchor point of the button.
|
||||
imageTransparency : number - The transparency of the image.
|
||||
]]
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Settings = script.Parent.Parent.Parent
|
||||
local Constants = require(Settings.Pages.LeaveGameScreen.Constants)
|
||||
|
||||
local GameSelector = Roact.PureComponent:extend("GameSelector")
|
||||
|
||||
function GameSelector:render()
|
||||
return Roact.createElement("ImageLabel", {
|
||||
Size = UDim2.new(0, 248, 0, 269),
|
||||
Position = self.props.position,
|
||||
AnchorPoint = self.props.anchorPoint,
|
||||
Image = Constants.Image.PLAY_NEXT_SELECTOR,
|
||||
ImageTransparency = self.props.imageTransparency,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = self.props.ZIndex,
|
||||
})
|
||||
end
|
||||
|
||||
return GameSelector
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
--[[
|
||||
A scrolling games list
|
||||
Props:
|
||||
focused : bool - Determines if the game list focused.
|
||||
zIndex : number - Determines the order of UI element rendering.
|
||||
onMoveSelection : function - The callback function for on the game selection changed.
|
||||
gamesList : array - An array of games. each element in the array is a map that must contain:
|
||||
{
|
||||
universeId : number,
|
||||
placeId : number,
|
||||
imageToken : string,
|
||||
}
|
||||
|
||||
]]
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local Settings = script.Parent.Parent.Parent
|
||||
local Modules = Settings.Parent
|
||||
local Common = Modules.Common
|
||||
local Analytics = require(Common.Analytics)
|
||||
local GameIcon = require(Settings.Components.PlayNextGame.GameIcon)
|
||||
|
||||
local GamesScrollList = Roact.PureComponent:extend("GamesScrollList")
|
||||
|
||||
-- NOTE: event context might need to be changed for other platforms.
|
||||
local EVENT_CONTEXT = "XboxOne"
|
||||
function GamesScrollList:init()
|
||||
self.pageLayout = Roact.createRef()
|
||||
self.analytics = Analytics.new()
|
||||
self.setSelection = function(index)
|
||||
if self.state.selectionIndex ~= index then
|
||||
local eventName = "MoveNextGameSelection"
|
||||
self.analytics.EventStream:setRBXEventStream(EVENT_CONTEXT, eventName)
|
||||
self:setState({
|
||||
selectionIndex = index
|
||||
})
|
||||
end
|
||||
self.props.onMoveSelection(index)
|
||||
end
|
||||
end
|
||||
|
||||
function GamesScrollList:render()
|
||||
local focused = self.props.focused
|
||||
local gamesList = self.props.gamesList
|
||||
local zIndex = self.props.zIndex
|
||||
local children = {}
|
||||
children.layout = Roact.createElement("UIPageLayout", {
|
||||
Animated = true,
|
||||
Circular = true,
|
||||
EasingDirection = Enum.EasingDirection.InOut,
|
||||
EasingStyle = Enum.EasingStyle.Quad,
|
||||
Padding = UDim.new(0, 20),
|
||||
TweenTime = 0.125,
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
GamepadInputEnabled = true,
|
||||
[Roact.Ref] = self.pageLayout,
|
||||
[Roact.Event.PageEnter] = function(rbx)
|
||||
self.pageLayout.current:ApplyLayout()
|
||||
end
|
||||
})
|
||||
-- NOTE:
|
||||
-- we want to display 5, but it looks janky when we wraps. So double the elements to
|
||||
-- fake the look of it wrapping. We can discuss with product about this. Maybe we can have
|
||||
-- 6 or 7 games instead or duplicating the games.
|
||||
--
|
||||
for i in ipairs(gamesList) do
|
||||
children[tostring(i)] = Roact.createElement(GameIcon, {
|
||||
universeId = gamesList[i].universeId,
|
||||
placeId = gamesList[i].placeId,
|
||||
imageToken = gamesList[i].imageToken,
|
||||
layoutOrder = i - 1,
|
||||
focused = focused and self.state.selectionIndex == i,
|
||||
onSelectionGained = function()
|
||||
self.setSelection(i)
|
||||
end,
|
||||
zIndex = zIndex,
|
||||
})
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(0, 1100, 1, 0),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
ClipsDescendants = true,
|
||||
}, {
|
||||
|
||||
FocusedItem = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(0, 200, 0, 200),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
}, children)
|
||||
})
|
||||
end
|
||||
|
||||
function GamesScrollList:didMount()
|
||||
self.pageLayout.current:JumpToIndex(0)
|
||||
-- UIPageLayout relies on child add order. This force applies the layout
|
||||
-- will not be needed with Quantum GUI
|
||||
self.pageLayout.current:ApplyLayout()
|
||||
if self.props.focused then
|
||||
delay(0, function()
|
||||
GuiService.SelectedCoreObject = self.pageLayout.current.CurrentPage
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function GamesScrollList:didUpdate(previousProps, previousState)
|
||||
if self.props.focused == previousProps.focused then
|
||||
return
|
||||
end
|
||||
if self.props.focused then
|
||||
GuiService.SelectedCoreObject = self.pageLayout.current.CurrentPage
|
||||
end
|
||||
end
|
||||
|
||||
return GamesScrollList
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
--[[
|
||||
A Roact play next game list section of the leave game screen.
|
||||
Props:
|
||||
screenId : Variant the screen id of this section.
|
||||
zIndex : number - Determines the order of UI element rendering.
|
||||
focused : bool - Determines if this section is focused.
|
||||
onLeaveNextGameList : function() - The callback function for when the selection leaves this section
|
||||
]]
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Otter = require(CorePackages.Otter)
|
||||
|
||||
local Settings = script.Parent.Parent.Parent
|
||||
local Modules = Settings.Parent
|
||||
local Constants = require(Settings.Pages.LeaveGameScreen.Constants)
|
||||
local GamesScrollList = require(Settings.Components.PlayNextGame.GamesScrollList)
|
||||
local GameSelector = require(Settings.Components.PlayNextGame.GameSelector)
|
||||
local Utility = require(Settings.Utility)
|
||||
local RobloxTranslator = require(Modules.RobloxTranslator)
|
||||
|
||||
local PlayNextGameXbox = Roact.PureComponent:extend("PlayNextGameXbox")
|
||||
|
||||
local FRAME_HIDE_HEIGHT = 375
|
||||
local FRAME_SHOW_HEIGHT = 547
|
||||
|
||||
local BG_HIDE_TRANSPARENCY = 1
|
||||
local BG_SHOW_TRANSPARENCY = 0.5
|
||||
|
||||
local SELECTOR_HIDE_TRANSPARENCY = 1
|
||||
local SELECTOR_SHOW_TRANSPARENCY = 0
|
||||
|
||||
local RECENTLY_PLAYED_LABEL = "Feature.SettingsHub.Label.RecentPlayed"
|
||||
|
||||
function PlayNextGameXbox:init()
|
||||
self.screenId = self.props.screenId
|
||||
self.ref = Roact.createRef()
|
||||
self.selectorRef = Roact.createRef()
|
||||
-- Initialize parameters for the spring motion
|
||||
self.motorOptions = {
|
||||
dampingRatio = 1,
|
||||
frequency = 5,
|
||||
}
|
||||
self.onMoveSelection = function(index)
|
||||
if self.state.currentSelection ~= index then
|
||||
self:setState({
|
||||
currentSelection = index
|
||||
})
|
||||
end
|
||||
end
|
||||
self.state =
|
||||
{
|
||||
currentSelection = 1,
|
||||
moveSelection = true,
|
||||
}
|
||||
end
|
||||
|
||||
function PlayNextGameXbox:render()
|
||||
local props = self.props
|
||||
local focused = props.focused
|
||||
local backgroundTransparency = 1
|
||||
local selectorTransparency = focused and 0 or 1
|
||||
local gamesdata = self.props.gamesData
|
||||
local size = UDim2.new(1, 0, 0, FRAME_HIDE_HEIGHT)
|
||||
local zIndex = self.props.zIndex + 1
|
||||
local indicatorArrow;
|
||||
local gameTitle;
|
||||
local gameStats;
|
||||
local gameSelector;
|
||||
local dividerSize = UDim2.new(0, 864, 0, 2)
|
||||
if focused then
|
||||
local upVotes = gamesdata[self.state.currentSelection].totalUpVotes
|
||||
local downVotes = gamesdata[self.state.currentSelection].totalDownVotes
|
||||
local gameRating = 0
|
||||
if upVotes + downVotes ~= 0 then
|
||||
gameRating = math.floor(((upVotes / (upVotes + downVotes)) * 100) + 0.5);
|
||||
end
|
||||
gameTitle = Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(1, 0, 0, 60),
|
||||
Position = UDim2.new(0.5, 0, 0, 355),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Text = gamesdata[self.state.currentSelection].name,
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size48,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
BackgroundTransparency = 1,
|
||||
TextWrapped = true,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
gameStats = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, 28),
|
||||
Position = UDim2.new(0.5, 0, 0, 420),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
},{
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
Padding = UDim.new(0, 15),
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
}),
|
||||
GameRating = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(0, 90, 0, 30),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 1,
|
||||
},{
|
||||
Icon = Roact.createElement("ImageLabel", {
|
||||
Size = UDim2.new(0, 28, 0, 28),
|
||||
Position = UDim2.new(0, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Image = Constants.Image.THUMB_ICON,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
Text = Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(0, 60, 0, 30),
|
||||
Position = UDim2.new(0, 36, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Text = tostring(gameRating)..' %',
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size24,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
}),
|
||||
NumOfPlayers = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(0, 100, 0, 30),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 2,
|
||||
},{
|
||||
Icon = Roact.createElement("ImageLabel", {
|
||||
Size = UDim2.new(0, 28, 0, 28),
|
||||
Position = UDim2.new(0, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Image = Constants.Image.PLAYER_NUMBER_ICON,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
Text = Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(0, 60, 1, 0),
|
||||
Position = UDim2.new(0, 36, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Text = tostring(gamesdata[self.state.currentSelection].playerCount),
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size24,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
gameSelector = Roact.createElement(GameSelector, {
|
||||
position = UDim2.new(0.5, 0, 0, 192),
|
||||
anchorPoint = Vector2.new(0.5, 0.5),
|
||||
imageTransparency = selectorTransparency,
|
||||
objRef = self.selectorRef,
|
||||
onActivated = self.onSelectGame,
|
||||
ZIndex = zIndex + 1,
|
||||
})
|
||||
else
|
||||
indicatorArrow = Roact.createElement("ImageLabel", {
|
||||
Image = Constants.Image.DOWN_ARROW,
|
||||
Size = UDim2.new(0, 24, 0, 12),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Position = UDim2.new(0.5, 0, 0, 40),
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
end
|
||||
return Roact.createElement("Frame", {
|
||||
Size = size,
|
||||
AnchorPoint = Vector2.new(0.5, 1),
|
||||
Position = UDim2.new(0.5, 0, 1, 0),
|
||||
BackgroundTransparency = backgroundTransparency,
|
||||
BackgroundColor3 = Color3.fromRGB(1, 1, 1),
|
||||
BorderSizePixel = 0,
|
||||
ZIndex = zIndex,
|
||||
[Roact.Ref] = self.ref,
|
||||
},{
|
||||
LeftDivider = Roact.createElement("Frame", {
|
||||
Size = dividerSize,
|
||||
AnchorPoint = Vector2.new(0, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
BorderSizePixel = 0,
|
||||
BackgroundColor3 = Constants.Color.WHITE,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
RightDivider = Roact.createElement("Frame", {
|
||||
Size = dividerSize,
|
||||
AnchorPoint = Vector2.new(1, 0),
|
||||
Position = UDim2.new(1, 0, 0, 0),
|
||||
BorderSizePixel = 0,
|
||||
BackgroundColor3 = Constants.Color.WHITE,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
NextGameListLabel = Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(1, 0, 0, 30),
|
||||
Position = UDim2.new(0.5, 0, 0, -3),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
--TODO: Need to be localized.
|
||||
Text = RobloxTranslator:FormatByKey(RECENTLY_PLAYED_LABEL),
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size24,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
BackgroundTransparency = 1,
|
||||
TextWrapped = true,
|
||||
TextXAlignment = "Center",
|
||||
TextYAlignment = "Center",
|
||||
ZIndex = zIndex + 1,
|
||||
}),
|
||||
IndicatorArrow = indicatorArrow,
|
||||
GamesList = Roact.createElement("Frame",{
|
||||
Position = UDim2.new(0.5, 0, 0, 66),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Size = UDim2.new(1, 0, 0, 232),
|
||||
BackgroundTransparency = 1,
|
||||
},{
|
||||
GamesScrollList = Roact.createElement(GamesScrollList,{
|
||||
focused = focused,
|
||||
onMoveSelection = self.onMoveSelection,
|
||||
gamesList = gamesdata,
|
||||
zIndex = zIndex,
|
||||
}),
|
||||
}),
|
||||
GameTitle = gameTitle,
|
||||
GameStats = gameStats,
|
||||
GameSelector = gameSelector,
|
||||
Redirect = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, 10),
|
||||
Position = UDim2.new(0.5, 0, 0, -100),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Selectable = true,
|
||||
BackgroundTransparency = 1,
|
||||
[Roact.Event.SelectionGained] = function()
|
||||
GuiService.SelectedCoreObject = nil
|
||||
self.props.onLeaveNextGameList()
|
||||
end
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
function PlayNextGameXbox:didMount()
|
||||
delay(0, function()
|
||||
if self.props.focused and self.ref.current then
|
||||
GuiService:AddSelectionParent(self.screenId, self.ref.current)
|
||||
end
|
||||
end)
|
||||
self.motor = Otter.createSingleMotor(0)
|
||||
self.motor:onStep(function(value)
|
||||
local t = value/100
|
||||
if self.ref.current ~= nil then
|
||||
self.ref.current.Size = UDim2.new(1, 0, 0, Utility:Round(Utility:Lerp(t, FRAME_HIDE_HEIGHT, FRAME_SHOW_HEIGHT)))
|
||||
self.ref.current.BackgroundTransparency = Utility:Lerp(t, BG_HIDE_TRANSPARENCY, BG_SHOW_TRANSPARENCY)
|
||||
end
|
||||
if self.selectorRef.current ~= nil then
|
||||
self.selectorRef.current.ImageTransparency = Utility:Lerp(t, SELECTOR_HIDE_TRANSPARENCY, SELECTOR_SHOW_TRANSPARENCY)
|
||||
end
|
||||
end)
|
||||
self.motor:start()
|
||||
end
|
||||
|
||||
function PlayNextGameXbox:didUpdate(previousProps, previousState)
|
||||
if self.props.focused == previousProps.focused then
|
||||
return
|
||||
end
|
||||
GuiService:RemoveSelectionGroup(self.screenId)
|
||||
if self.props.focused and self.ref.current then
|
||||
GuiService:AddSelectionParent(self.screenId, self.ref.current)
|
||||
end
|
||||
local targetPercentage = self.props.focused and 100 or 0
|
||||
self.motor:setGoal(Otter.spring(targetPercentage, self.motorOptions))
|
||||
end
|
||||
|
||||
function PlayNextGameXbox:willUnmount()
|
||||
GuiService:RemoveSelectionGroup(self.screenId)
|
||||
self.motor:destroy()
|
||||
end
|
||||
|
||||
return PlayNextGameXbox
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
--[[
|
||||
Creates a Roact component that is a grey rounded button
|
||||
Props:
|
||||
position : UDim2 - Position of the button.
|
||||
zIndex : number - Determines the order of UI element rendering.
|
||||
anchorPoint : UDim2 - The anchor point of the button.
|
||||
onResume : function() - Fires when the button is activated.
|
||||
]]
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Settings = script.Parent.Parent
|
||||
local Modules = Settings.Parent
|
||||
local Constants = require(Settings.Pages.LeaveGameScreen.Constants)
|
||||
local RoundedSelectionImage = require(Settings.Common.RoundedSelectionImage)
|
||||
local RobloxTranslator = require(Modules.RobloxTranslator)
|
||||
|
||||
local ResumeButton = Roact.PureComponent:extend("ResumeButton")
|
||||
|
||||
local RESUME_GAME_BUTTON_LABEL = "Feature.SettingsHub.Label.DontLeaveButton"
|
||||
|
||||
function ResumeButton:init()
|
||||
self.onSelectionGained = function()
|
||||
self:setState({
|
||||
focused = true
|
||||
})
|
||||
end
|
||||
self.onSelectionLost = function()
|
||||
self:setState({
|
||||
focused = false
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function ResumeButton:render()
|
||||
local size = UDim2.new(0, 320, 0, 80)
|
||||
local position = self.props.position or UDim2.new(0.5, 0, 0.5, 0)
|
||||
local anchorPoint = self.props.anchorPoint or Vector2.new(0.5, 0.5)
|
||||
local zIndex = self.props.zIndex
|
||||
local text = RobloxTranslator:FormatByKey(RESUME_GAME_BUTTON_LABEL)
|
||||
local buttonColor = Constants.Color.GREY
|
||||
local textColor = Constants.Color.WHITE
|
||||
local selector = RoundedSelectionImage()
|
||||
if self.state.focused then
|
||||
buttonColor = Constants.Color.BLUE
|
||||
textColor = Constants.Color.DARK
|
||||
end
|
||||
local label = Roact.createElement("TextLabel",{
|
||||
Text = text,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Font = Enum.Font.SourceSans,
|
||||
TextSize = Constants.TextSize.BUTTON,
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
TextColor3 = textColor,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex + 1
|
||||
})
|
||||
return Roact.createElement("ImageButton",{
|
||||
Image = Constants.Image.BUTTON,
|
||||
Size = size,
|
||||
Position = position,
|
||||
AnchorPoint = anchorPoint,
|
||||
[Roact.Event.SelectionGained] = self.onSelectionGained,
|
||||
[Roact.Event.SelectionLost] = self.onSelectionLost,
|
||||
[Roact.Event.Activated] = self.props.onResume,
|
||||
ImageColor3 = buttonColor,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(8, 8, 9, 9),
|
||||
SelectionImageObject = selector,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex
|
||||
},{
|
||||
Label = label,
|
||||
})
|
||||
end
|
||||
return ResumeButton
|
||||
@@ -0,0 +1,44 @@
|
||||
--[[
|
||||
{
|
||||
universeId : number,
|
||||
placeId : number,
|
||||
name : string,
|
||||
playerCount : number,
|
||||
imageToken : string,
|
||||
totalUpVotes : number,
|
||||
totalDownVotes : number,
|
||||
}
|
||||
]]
|
||||
|
||||
local Game = {}
|
||||
|
||||
function Game.new()
|
||||
local self = {}
|
||||
return self
|
||||
end
|
||||
|
||||
function Game.fromGameCache(gameCache)
|
||||
local self = Game.new()
|
||||
self.universeId = gameCache.UniverseId
|
||||
self.placeId = gameCache.PlaceId
|
||||
self.name = gameCache.Name
|
||||
self.playerCount = gameCache.PlayerCount
|
||||
self.imageToken = gameCache.ImageToken
|
||||
self.totalUpVotes = gameCache.VoteData.UpVotes
|
||||
self.totalDownVotes = gameCache.VoteData.DownVotes
|
||||
return self
|
||||
end
|
||||
|
||||
function Game.fromJsonData(gameJson)
|
||||
local self = Game.new()
|
||||
self.universeId = gameJson.universeId
|
||||
self.placeId = gameJson.placeId
|
||||
self.name = gameJson.name
|
||||
self.playerCount = gameJson.playerCount
|
||||
self.imageToken = gameJson.imageToken
|
||||
self.totalUpVotes = gameJson.totalUpVotes
|
||||
self.totalDownVotes = gameJson.totalDownVotes
|
||||
return self
|
||||
end
|
||||
|
||||
return Game
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,654 @@
|
||||
--[[
|
||||
Filename: Help.lua
|
||||
Written by: jeditkacheff
|
||||
Version 1.0
|
||||
Description: Takes care of the help page in Settings Menu
|
||||
--]]
|
||||
|
||||
------------ FFLAGS -------------------
|
||||
local success, result = pcall(function() return settings():GetFFlag('UseNotificationsLocalization') end)
|
||||
local FFlagUseNotificationsLocalization = success and result
|
||||
|
||||
-------------- CONSTANTS --------------
|
||||
local KEYBOARD_MOUSE_TAG = "KeyboardMouse"
|
||||
local TOUCH_TAG = "Touch"
|
||||
local GAMEPAD_TAG = "Gamepad"
|
||||
local PC_TABLE_SPACING = 4
|
||||
local XBOX_CONTROLLER_IMAGE_OFFSET = 30
|
||||
local TEXT_EDGE_DISTANCE = 20
|
||||
|
||||
-------------- SERVICES --------------
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local TextService = game:GetService("TextService")
|
||||
local Settings = UserSettings()
|
||||
local GameSettings = Settings.GameSettings
|
||||
|
||||
----------- UTILITIES --------------
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
|
||||
------------ Variables -------------------
|
||||
local PageInstance = nil
|
||||
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
|
||||
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
|
||||
local function Initialize()
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
this.HelpPages = {}
|
||||
this.HelpPageContents = {}
|
||||
this.ActiveHelpScheme = nil
|
||||
|
||||
local lastInputType = nil
|
||||
|
||||
function this:GetCurrentInputType()
|
||||
if lastInputType == nil then -- we don't know what controls the user has, just use reasonable defaults
|
||||
local platform = UserInputService:GetPlatform()
|
||||
if platform == Enum.Platform.XBoxOne or platform == Enum.Platform.WiiU then
|
||||
|
||||
return GAMEPAD_TAG
|
||||
elseif platform == Enum.Platform.Windows or platform == Enum.Platform.OSX then
|
||||
return KEYBOARD_MOUSE_TAG
|
||||
else
|
||||
return TOUCH_TAG
|
||||
end
|
||||
end
|
||||
|
||||
if lastInputType == Enum.UserInputType.Keyboard or lastInputType == Enum.UserInputType.MouseMovement or
|
||||
lastInputType == Enum.UserInputType.MouseButton1 or lastInputType == Enum.UserInputType.MouseButton2 or
|
||||
lastInputType == Enum.UserInputType.MouseButton3 or lastInputType == Enum.UserInputType.MouseWheel then
|
||||
return KEYBOARD_MOUSE_TAG
|
||||
elseif lastInputType == Enum.UserInputType.Touch then
|
||||
return TOUCH_TAG
|
||||
elseif lastInputType == Enum.UserInputType.Gamepad1 or lastInputType == Enum.UserInputType.Gamepad2 or
|
||||
lastInputType == Enum.UserInputType.Gamepad3 or lastInputType == Enum.UserInputType.Gamepad4 then
|
||||
return GAMEPAD_TAG
|
||||
end
|
||||
|
||||
return KEYBOARD_MOUSE_TAG
|
||||
end
|
||||
|
||||
|
||||
local function createPCHelp(parentFrame)
|
||||
local function createPCGroup(title, actionInputBindings)
|
||||
local textIndent = 9
|
||||
|
||||
local pcGroupFrame = utility:Create'Frame'
|
||||
{
|
||||
Size = UDim2.new(1/3,-PC_TABLE_SPACING,1,0),
|
||||
BackgroundTransparency = 1,
|
||||
Name = "PCGroupFrame" .. tostring(title)
|
||||
};
|
||||
local pcGroupTitle = utility:Create'TextLabel'
|
||||
{
|
||||
Position = UDim2.new(0,textIndent,0,0),
|
||||
Size = UDim2.new(1,-textIndent,0,30),
|
||||
BackgroundTransparency = 1,
|
||||
Text = title,
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size18,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Name = "PCGroupTitle" .. tostring(title),
|
||||
ZIndex = 2,
|
||||
Parent = pcGroupFrame
|
||||
};
|
||||
|
||||
local count = 0
|
||||
local frameHeight = 42
|
||||
local spacing = 2
|
||||
local offset = pcGroupTitle.Size.Y.Offset
|
||||
for i = 1, #actionInputBindings do
|
||||
for actionName, inputName in pairs(actionInputBindings[i]) do
|
||||
local actionInputFrame = utility:Create'Frame'
|
||||
{
|
||||
Size = UDim2.new(1,0,0,frameHeight),
|
||||
Position = UDim2.new(0,0,0, offset + ((frameHeight + spacing) * count)),
|
||||
BackgroundTransparency = 0.65,
|
||||
BorderSizePixel = 0,
|
||||
ZIndex = 2,
|
||||
Name = "ActionInputBinding" .. tostring(actionName),
|
||||
Parent = pcGroupFrame
|
||||
};
|
||||
|
||||
local nameLabel = utility:Create'TextLabel'
|
||||
{
|
||||
Size = UDim2.new(0.4,-textIndent,0,frameHeight),
|
||||
Position = UDim2.new(0,textIndent,0,0),
|
||||
BackgroundTransparency = 1,
|
||||
Text = actionName,
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size18,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Name = actionName .. "Label",
|
||||
ZIndex = 2,
|
||||
Parent = actionInputFrame,
|
||||
TextWrapped = true,
|
||||
TextScaled = true
|
||||
};
|
||||
do
|
||||
local textSizeConstraint = Instance.new("UITextSizeConstraint",nameLabel)
|
||||
textSizeConstraint.MaxTextSize = 18
|
||||
end
|
||||
|
||||
|
||||
local inputLabel = utility:Create'TextLabel'
|
||||
{
|
||||
Size = UDim2.new(0.5,0,0,frameHeight),
|
||||
Position = UDim2.new(0.5,-4,0,0),
|
||||
BackgroundTransparency = 1,
|
||||
Text = inputName,
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size18,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Name = inputName .. "Label",
|
||||
ZIndex = 2,
|
||||
Parent = actionInputFrame,
|
||||
TextWrapped = true,
|
||||
TextScaled = true
|
||||
};
|
||||
do
|
||||
local textSizeConstraint = Instance.new("UITextSizeConstraint",inputLabel)
|
||||
textSizeConstraint.MaxTextSize = 18
|
||||
end
|
||||
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
pcGroupFrame.Size = UDim2.new(pcGroupFrame.Size.X.Scale,pcGroupFrame.Size.X.Offset,
|
||||
0, offset + ((frameHeight + spacing) * count))
|
||||
|
||||
return pcGroupFrame
|
||||
end
|
||||
|
||||
local rowOffset = 50
|
||||
local isOSX = UserInputService:GetPlatform() == Enum.Platform.OSX
|
||||
|
||||
local charMoveFrame = createPCGroup( "Character Movement", {[1] = {["Move Forward"] = "W/Up Arrow"},
|
||||
[2] = {["Move Backward"] = "S/Down Arrow"},
|
||||
[3] = {["Move Left"] = "A/Left Arrow"},
|
||||
[4] = {["Move Right"] = "D/Right Arrow"},
|
||||
[5] = {["Jump"] = "Space"}} )
|
||||
charMoveFrame.Parent = parentFrame
|
||||
|
||||
local accessoriesFrame = createPCGroup("Accessories", {
|
||||
[1] = {["Equip Tools"] = "1,2,3..."},
|
||||
[2] = {["Unequip Tools"] = "1,2,3..."},
|
||||
[3] = {["Drop Tool"] = "Backspace"},
|
||||
[4] = {["Use Tool"] = "Left Mouse Button"} })
|
||||
accessoriesFrame.Position = UDim2.new(1/3,PC_TABLE_SPACING,0,0)
|
||||
accessoriesFrame.Parent = parentFrame
|
||||
|
||||
local miscFrame = nil
|
||||
miscFrame = createPCGroup("Misc", {
|
||||
[1] = {["Screenshot"] = "Print Screen"},
|
||||
[2] = {["Record Video"] = isOSX and "F12/fn + F12" or "F12"},
|
||||
[3] = {["Dev Console"] = isOSX and "F9/fn + F9" or "F9"},
|
||||
[4] = {["Mouselock"] = "Shift"},
|
||||
[5] = {["Graphics Level"] = isOSX and "F10/fn + F10" or "F10"},
|
||||
[6] = {["Fullscreen"] = isOSX and "F11/fn + F11" or "F11"},
|
||||
[7] = {["Perf. Stats"] = isOSX and "Fn+Opt+Cmd+F7" or "Ctrl + Shift + F7"},
|
||||
}
|
||||
)
|
||||
|
||||
miscFrame.Position = UDim2.new(2/3,PC_TABLE_SPACING * 2,0,0)
|
||||
miscFrame.Parent = parentFrame
|
||||
|
||||
local camFrame = createPCGroup("Camera Movement", { [1] = {["Rotate"] = "Right Mouse Button"},
|
||||
[2] = {["Zoom In/Out"] = "Mouse Wheel"},
|
||||
[3] = {["Zoom In"] = "I"},
|
||||
[4] = {["Zoom Out"] = "O"} })
|
||||
camFrame.Position = UDim2.new(0,0,charMoveFrame.Size.Y.Scale,charMoveFrame.Size.Y.Offset + rowOffset)
|
||||
camFrame.Parent = parentFrame
|
||||
|
||||
local menuFrame = createPCGroup("Menu Items", { [1] = {["Roblox Menu"] = "ESC"},
|
||||
[2] = {["Backpack"] = "~"},
|
||||
[3] = {["Playerlist"] = "TAB"},
|
||||
[4] = {["Chat"] = "/"} })
|
||||
menuFrame.Position = UDim2.new(1/3,PC_TABLE_SPACING,charMoveFrame.Size.Y.Scale,charMoveFrame.Size.Y.Offset + rowOffset)
|
||||
menuFrame.Parent = parentFrame
|
||||
|
||||
parentFrame.Size = UDim2.new(parentFrame.Size.X.Scale, parentFrame.Size.X.Offset, 0,
|
||||
menuFrame.Size.Y.Offset + menuFrame.Position.Y.Offset)
|
||||
end
|
||||
|
||||
local function createGamepadHelp(parentFrame)
|
||||
local gamepadImage = nil
|
||||
local imageSize = nil
|
||||
|
||||
gamepadImage = "rbxasset://textures/ui/Settings/Help/GenericController.png"
|
||||
imageSize = UDim2.new(0, 473, 0, 287)
|
||||
|
||||
local imagePosition = UDim2.new(0.5, -imageSize.X.Offset/2, 0.5, -imageSize.Y.Offset/2)
|
||||
|
||||
if UserInputService:GetPlatform() == Enum.Platform.XBoxOne or UserInputService:GetPlatform() == Enum.Platform.XBox360 then
|
||||
gamepadImage = "rbxasset://textures/ui/Settings/Help/XboxController.png"
|
||||
imageSize = UDim2.new(0, 745, 0, 452)
|
||||
imagePosition = UDim2.new(0.5, (-imageSize.X.Offset/2) + XBOX_CONTROLLER_IMAGE_OFFSET, 0.5, -imageSize.Y.Offset/2 + 7)
|
||||
elseif UserInputService:GetPlatform() == Enum.Platform.PS4 or UserInputService:GetPlatform() == Enum.Platform.PS3 then
|
||||
gamepadImage = "rbxasset://textures/ui/Settings/Help/PSController.png"
|
||||
end
|
||||
|
||||
local gamepadImageLabel = utility:Create'ImageLabel'
|
||||
{
|
||||
Name = "GamepadImage",
|
||||
Size = imageSize,
|
||||
Position = imagePosition,
|
||||
Image = gamepadImage,
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = 2,
|
||||
Parent = parentFrame
|
||||
};
|
||||
parentFrame.Size = UDim2.new(parentFrame.Size.X.Scale, parentFrame.Size.X.Offset, 0, gamepadImageLabel.Size.Y.Offset + 100)
|
||||
|
||||
local gamepadFontSize = isTenFootInterface and Enum.FontSize.Size36 or Enum.FontSize.Size24
|
||||
local textVerticalSize = (gamepadFontSize == Enum.FontSize.Size36) and 36 or 24
|
||||
local function createGamepadLabel(text, position, size, rightAligned)
|
||||
local nameLabel = nil
|
||||
if FFlagUseNotificationsLocalization == true then
|
||||
nameLabel = utility:Create'TextLabel'{
|
||||
Position = position,
|
||||
Size = size,
|
||||
BackgroundTransparency = 1,
|
||||
Text = text,
|
||||
TextXAlignment = rightAligned and Enum.TextXAlignment.Right or Enum.TextXAlignment.Left,
|
||||
AnchorPoint = rightAligned and Vector2.new(1, 0.5) or Vector2.new(0, 0.5),
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = gamepadFontSize,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
Name = text .. "Label",
|
||||
ZIndex = 2,
|
||||
Parent = gamepadImageLabel,
|
||||
TextScaled = true,
|
||||
TextWrapped = true
|
||||
};
|
||||
else
|
||||
nameLabel = utility:Create'TextLabel'{
|
||||
Position = position,
|
||||
Size = size,
|
||||
BackgroundTransparency = 1,
|
||||
Text = text,
|
||||
TextXAlignment = rightAligned and Enum.TextXAlignment.Right or Enum.TextXAlignment.Left,
|
||||
AnchorPoint = rightAligned and Vector2.new(1, 0.5) or Vector2.new(0, 0.5),
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = gamepadFontSize,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
Name = text .. "Label",
|
||||
ZIndex = 2,
|
||||
Parent = gamepadImageLabel
|
||||
};
|
||||
end
|
||||
|
||||
nameLabel.TextWrapped = true
|
||||
|
||||
local textSize = TextService:GetTextSize(text, textVerticalSize, Enum.Font.SourceSansBold, Vector2.new(0, 0))
|
||||
local minSizeXOffset = textSize.X
|
||||
local distanceToCenter = math.abs(position.X.Offset)
|
||||
local parentGui = (gamepadImage == "rbxasset://textures/ui/Settings/Help/XboxController.png") and RobloxGui or parentFrame
|
||||
|
||||
local function updateNameLabelSize()
|
||||
local nameLabelSizeXOffset = nameLabel.Size.X.Offset
|
||||
if gamepadImage == "rbxasset://textures/ui/Settings/Help/XboxController.png" then
|
||||
nameLabelSizeXOffset = rightAligned and
|
||||
RobloxGui.AbsoluteSize.X/2 + XBOX_CONTROLLER_IMAGE_OFFSET - distanceToCenter - TEXT_EDGE_DISTANCE or
|
||||
RobloxGui.AbsoluteSize.X/2 - XBOX_CONTROLLER_IMAGE_OFFSET - distanceToCenter - TEXT_EDGE_DISTANCE
|
||||
else
|
||||
nameLabelSizeXOffset = parentFrame.AbsoluteSize.X/2 - distanceToCenter
|
||||
end
|
||||
|
||||
if nameLabelSizeXOffset < minSizeXOffset then
|
||||
nameLabel.Size = UDim2.new(nameLabel.Size.X.Scale, nameLabelSizeXOffset, nameLabel.Size.Y.Scale, textVerticalSize * 2)
|
||||
nameLabel.TextScaled = true
|
||||
else
|
||||
nameLabel.Size = UDim2.new(nameLabel.Size.X.Scale, nameLabelSizeXOffset, nameLabel.Size.Y.Scale, textVerticalSize)
|
||||
nameLabel.FontSize = gamepadFontSize
|
||||
nameLabel.TextScaled = false
|
||||
end
|
||||
end
|
||||
|
||||
local nameLabelChangeCn = parentGui:GetPropertyChangedSignal('AbsoluteSize'):connect(function()
|
||||
updateNameLabelSize()
|
||||
end)
|
||||
|
||||
updateNameLabelSize()
|
||||
end
|
||||
|
||||
if gamepadImage == "rbxasset://textures/ui/Settings/Help/XboxController.png" then
|
||||
createGamepadLabel("Switch Tool", UDim2.new(0.5, -390, 0, 0), UDim2.new(0, 100, 0, textVerticalSize), true)
|
||||
createGamepadLabel("Game Menu Toggle", UDim2.new(0.5, -390, 0.15, 0), UDim2.new(0, 164, 0, textVerticalSize), true)
|
||||
createGamepadLabel("Move", UDim2.new(0.5, -390, 0.31, 0), UDim2.new(0, 46, 0, textVerticalSize), true)
|
||||
createGamepadLabel("Menu Navigation", UDim2.new(0.5, -390, 0.46, 0), UDim2.new(0, 164, 0, textVerticalSize), true)
|
||||
|
||||
createGamepadLabel("Use Tool", UDim2.new(0.5, 330, 0, 0), UDim2.new(0, 73, 0, textVerticalSize))
|
||||
createGamepadLabel("Roblox Menu", UDim2.new(0.5, 330, 0.15, 0), UDim2.new(0, 122, 0, textVerticalSize))
|
||||
createGamepadLabel("Back", UDim2.new(0.5, 330, 0.31, 0), UDim2.new(0, 43, 0, textVerticalSize))
|
||||
createGamepadLabel("Jump", UDim2.new(0.5, 330, 0.46, 0), UDim2.new(0, 49, 0, textVerticalSize))
|
||||
createGamepadLabel("Rotate Camera", UDim2.new(0.5, 380, 0.62, 0), UDim2.new(0, 132, 0, textVerticalSize))
|
||||
createGamepadLabel("Camera Zoom", UDim2.new(0.5, 380, 0.77, 0), UDim2.new(0, 122, 0, textVerticalSize))
|
||||
else
|
||||
createGamepadLabel("Switch Tool", UDim2.new(0.5, -250, 0, 0), UDim2.new(0, 100, 0, textVerticalSize), true)
|
||||
createGamepadLabel("Game Menu Toggle", UDim2.new(0.5, -250, 0.15, 0), UDim2.new(0, 164, 0, textVerticalSize), true)
|
||||
createGamepadLabel("Move", UDim2.new(0.5, -250, 0.31, 0), UDim2.new(0, 46, 0, textVerticalSize), true)
|
||||
createGamepadLabel("Menu Navigation", UDim2.new(0.5, -250, 0.46, 0), UDim2.new(0, 143, 0, textVerticalSize), true)
|
||||
|
||||
createGamepadLabel("Use Tool", UDim2.new(0.5, 215, 0, 0), UDim2.new(0, 73, 0, textVerticalSize))
|
||||
createGamepadLabel("Roblox Menu", UDim2.new(0.5, 215, 0.15, 0), UDim2.new(0, 122, 0, textVerticalSize))
|
||||
createGamepadLabel("Back", UDim2.new(0.5, 215, 0.31, 0), UDim2.new(0, 43, 0, textVerticalSize))
|
||||
createGamepadLabel("Jump", UDim2.new(0.5, 215, 0.46, 0), UDim2.new(0, 49, 0, textVerticalSize))
|
||||
createGamepadLabel("Rotate Camera", UDim2.new(0.5, 255, 0.62, 0), UDim2.new(0, 132, 0, textVerticalSize))
|
||||
createGamepadLabel("Camera Zoom", UDim2.new(0.5, 255, 0.77, 0), UDim2.new(0, 122, 0, textVerticalSize))
|
||||
end
|
||||
-- NOTE: On consoles we put the dev console in the settings menu. Only place
|
||||
-- owners can see this for now.
|
||||
end
|
||||
|
||||
local function updateTouchLayout(scheme) -- adjust layout to work well with the various touch control schemes
|
||||
this.ActiveHelpScheme = scheme
|
||||
local isPortrait = utility:IsPortrait()
|
||||
local helpFrame = this.HelpPages[TOUCH_TAG]
|
||||
|
||||
if helpFrame then
|
||||
local helpElements = this.HelpPageContents[TOUCH_TAG]
|
||||
|
||||
local function hideUneeded(list)
|
||||
if list then
|
||||
for name, item in pairs(helpElements) do
|
||||
item.Visible = not list[name]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local hidden
|
||||
if scheme == Enum.TouchMovementMode.DynamicThumbstick then
|
||||
-- show that movement is done by dragging
|
||||
-- show that tapping on bottom of the screen is to jump
|
||||
-- show that tapping on the top of the screen is to use tools
|
||||
-- show that dragging on the top of the screen is to pan camera
|
||||
hidden = {MoveImageCTM = true}
|
||||
helpElements["MoveLabel"].Position = isPortrait and UDim2.new(0.25,-helpElements["MoveLabel"].AbsoluteSize.x/2,0.75,-50) or UDim2.new(0.15,-helpElements["MoveLabel"].AbsoluteSize.x/2,0.85,-helpElements["MoveLabel"].AbsoluteSize.y)
|
||||
helpElements["JumpLabel"].Position = isPortrait and UDim2.new(0.75,-helpElements["JumpLabel"].AbsoluteSize.x/2,0.75,-50) or UDim2.new(0.85,-60,0.85,-helpElements["JumpLabel"].AbsoluteSize.y)
|
||||
helpElements["RotateLabel"].Position = isPortrait and UDim2.new(1,-helpElements["RotateLabel"].AbsoluteSize.x-20,0.02,0) or UDim2.new(0.85,-helpElements["RotateLabel"].AbsoluteSize.x/2,0.02,0)
|
||||
helpElements["UseToolLabel"].Position = isPortrait and UDim2.new(0.5,-helpElements["UseToolLabel"].AbsoluteSize.x/2,0.5,-100) or UDim2.new(0.5,-helpElements["UseToolLabel"].AbsoluteSize.x/2,0.5,-60)
|
||||
|
||||
helpElements["EquipLabel"].Position = isPortrait and UDim2.new(0.5,-60,0.75,50) or UDim2.new(0.5,-60,0.64,0)
|
||||
helpElements["ZoomLabel"].Position = isPortrait and UDim2.new(0,20,0.02,0) or UDim2.new(0.15,-60,0.02,0)
|
||||
|
||||
elseif scheme == Enum.TouchMovementMode.ClickToMove then
|
||||
-- show that dragging on the screen is to pan the camera
|
||||
-- show that tapping is to move
|
||||
hidden = {BottomHalfDisplay = true, MoveImageDTS = true, JumpLabel = true} -- cant manually jump on ctm
|
||||
helpElements["MoveLabel"].Position = isPortrait and UDim2.new(0.25,-helpElements["MoveLabel"].AbsoluteSize.x/2,0.5,0) or UDim2.new(0.25,-helpElements["MoveLabel"].AbsoluteSize.x/2,0.5,40)
|
||||
helpElements["RotateLabel"].Position = isPortrait and UDim2.new(1,-helpElements["RotateLabel"].AbsoluteSize.x-20,0.02,0) or UDim2.new(0.5,-60,0.02,0)
|
||||
helpElements["UseToolLabel"].Position = isPortrait and UDim2.new(0.75,-helpElements["UseToolLabel"].AbsoluteSize.x/2,0.5,0) or UDim2.new(0.85,-60,0.02,0)
|
||||
|
||||
helpElements["EquipLabel"].Position = isPortrait and UDim2.new(0.5,-60,0.75,50) or UDim2.new(0.5,-60,0.64,0)
|
||||
helpElements["ZoomLabel"].Position = isPortrait and UDim2.new(0,20,0.02,0) or UDim2.new(0.15,-60,0.02,0)
|
||||
else
|
||||
-- keep the default style, but take portrait mode into account
|
||||
hidden = {BottomHalfDisplay = true, MoveImageDTS = true, MoveImageCTM = true, JumpImage = true} -- if theres a jump button we don't need to do touch gestures, same with thumbstick
|
||||
helpElements["MoveLabel"].Position = isPortrait and UDim2.new(0.06,0,1,-120) or UDim2.new(0.06,0,0.58,0)
|
||||
helpElements["JumpLabel"].Position = isPortrait and UDim2.new(0.94,-helpElements["JumpLabel"].AbsoluteSize.x,1,-120) or UDim2.new(0.8,0,0.58,0)
|
||||
helpElements["RotateLabel"].Position = isPortrait and UDim2.new(1,-helpElements["RotateLabel"].AbsoluteSize.x-20,0.02,0) or UDim2.new(0.5,-60,0.02,0)
|
||||
helpElements["UseToolLabel"].Position = isPortrait and UDim2.new(.5,-helpElements["UseToolLabel"].AbsoluteSize.x/2,0.25,50) or UDim2.new(0.85,-60,0.02,0)
|
||||
|
||||
helpElements["EquipLabel"].Position = isPortrait and UDim2.new(0.5,-60,0.75,50) or UDim2.new(0.5,-60,0.64,0)
|
||||
helpElements["ZoomLabel"].Position = isPortrait and UDim2.new(0,20,0.02,0) or UDim2.new(0.15,-60,0.02,0)
|
||||
end
|
||||
hideUneeded(hidden)
|
||||
end
|
||||
end
|
||||
|
||||
local function createTouchHelp(parentFrame)
|
||||
local createdElements = {} -- dictionary of buttons created
|
||||
local smallScreen = utility:IsSmallTouchScreen()
|
||||
local ySize = GuiService:GetScreenResolution().y - 350
|
||||
if smallScreen then
|
||||
ySize = GuiService:GetScreenResolution().y - 100
|
||||
end
|
||||
parentFrame.Size = UDim2.new(1,0,0,ySize)
|
||||
|
||||
local function createDisplayFrame(name, position, size, transparency, color, parent)
|
||||
local frame = utility:Create'Frame'
|
||||
{
|
||||
BackgroundColor3 = color,
|
||||
Position = position,
|
||||
Size = size,
|
||||
BackgroundTransparency = transparency,
|
||||
Name = name,
|
||||
ZIndex = 1,
|
||||
Parent = parent
|
||||
};
|
||||
return frame
|
||||
end
|
||||
|
||||
local function createTouchLabel(text, position, size, parent)
|
||||
local nameLabel = utility:Create'TextLabel'
|
||||
{
|
||||
Position = position,
|
||||
Size = size,
|
||||
BackgroundTransparency = 1,
|
||||
Text = text,
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size14,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
Name = text .. "Label",
|
||||
ZIndex = 3,
|
||||
Parent = parent,
|
||||
TextScaled = true,
|
||||
TextWrapped = true
|
||||
};
|
||||
|
||||
if not smallScreen then
|
||||
nameLabel.FontSize = Enum.FontSize.Size18
|
||||
nameLabel.Size = UDim2.new(nameLabel.Size.X.Scale, nameLabel.Size.X.Offset, nameLabel.Size.Y.Scale, nameLabel.Size.Y.Offset + 4)
|
||||
end
|
||||
|
||||
local nameBackgroundImage = utility:Create'ImageLabel'
|
||||
{
|
||||
Name = text .. "BackgroundImage",
|
||||
Size = UDim2.new(1.25,0,1.25,0),
|
||||
Position = UDim2.new(-0.125,0,-0.065,0),
|
||||
BackgroundTransparency = 1,
|
||||
Image = "rbxasset://textures/ui/Settings/Radial/RadialLabel.png",
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(12,2,65,21),
|
||||
ZIndex = 2,
|
||||
Parent = nameLabel
|
||||
};
|
||||
|
||||
local textSizeConstraint = Instance.new("UITextSizeConstraint",nameLabel)
|
||||
textSizeConstraint.MaxTextSize = 18
|
||||
|
||||
return nameLabel
|
||||
end
|
||||
|
||||
local function createTouchGestureImage(name, image, position, size, parent)
|
||||
local gestureImage = utility:Create'ImageLabel'
|
||||
{
|
||||
Name = name,
|
||||
Size = size,
|
||||
Position = position,
|
||||
BackgroundTransparency = 1,
|
||||
Image = image,
|
||||
ZIndex = 2,
|
||||
Parent = parent
|
||||
};
|
||||
|
||||
return gestureImage
|
||||
end
|
||||
|
||||
local xSizeOffset = 30
|
||||
local ySize = 25
|
||||
if smallScreen then xSizeOffset = 0 end
|
||||
|
||||
createdElements["BottomHalfDisplay"] = createDisplayFrame("BottomHalfFrame", UDim2.new(0,0,0.5,-16), UDim2.new(1,0,1,0), 0.35, Color3.new(0,0,0), parentFrame)
|
||||
|
||||
-- movement stuff
|
||||
createdElements["MoveLabel"] = createTouchLabel("Move", UDim2.new(0.06,0,0.58,0), UDim2.new(0,77 + xSizeOffset,0,ySize), parentFrame)
|
||||
createdElements["MoveImageDTS"] = createTouchGestureImage("MoveImageDTS", "rbxasset://textures/ui/Settings/Help/RotateCameraGesture.png", UDim2.new(0.5,-32,1,3), UDim2.new(0,65,0,48), createdElements["MoveLabel"])
|
||||
createdElements["MoveImageCTM"] = createTouchGestureImage("MoveImageCTM", "rbxasset://textures/ui/Settings/Help/UseToolGesture.png", UDim2.new(0.5,-19,1,3), UDim2.new(0,38,0,52), createdElements["MoveLabel"])
|
||||
|
||||
-- jumping stuff
|
||||
createdElements["JumpLabel"] = createTouchLabel("Jump", UDim2.new(0.8,0,0.58,0), UDim2.new(0,77 + xSizeOffset,0,ySize), parentFrame)
|
||||
createdElements["JumpImage"] = createTouchGestureImage("JumpImage", "rbxasset://textures/ui/Settings/Help/UseToolGesture.png", UDim2.new(0.5,-19,1,3), UDim2.new(0,38,0,52), createdElements["JumpLabel"])
|
||||
|
||||
createdElements["EquipLabel"] = createTouchLabel("Equip/Unequip Tools", UDim2.new(0.5,-60,0.64,0), UDim2.new(0,120 + xSizeOffset,0,ySize), parentFrame)
|
||||
|
||||
createdElements["ZoomLabel"] = createTouchLabel("Zoom In/Out", UDim2.new(0.15,-60,0.02,0), UDim2.new(0,120,0,ySize), parentFrame)
|
||||
createdElements["ZoomImage"] = createTouchGestureImage("ZoomImage", "rbxasset://textures/ui/Settings/Help/ZoomGesture.png", UDim2.new(0.5,-26,1,3), UDim2.new(0,53,0,59), createdElements["ZoomLabel"])
|
||||
createdElements["RotateLabel"] = createTouchLabel("Rotate Camera", UDim2.new(0.5,-60,0.02,0), UDim2.new(0,120,0,ySize), parentFrame)
|
||||
createdElements["RotateImage"] = createTouchGestureImage("RotateImage", "rbxasset://textures/ui/Settings/Help/RotateCameraGesture.png", UDim2.new(0.5,-32,1,3), UDim2.new(0,65,0,48), createdElements["RotateLabel"])
|
||||
createdElements["UseToolLabel"] = createTouchLabel("Use Tool", UDim2.new(0.85,-60,0.02,0), UDim2.new(0,120,0,ySize), parentFrame)
|
||||
createdElements["ToolImage"] = createTouchGestureImage("ToolImage", "rbxasset://textures/ui/Settings/Help/UseToolGesture.png", UDim2.new(0.5,-19,1,3), UDim2.new(0,38,0,52), createdElements["UseToolLabel"])
|
||||
|
||||
return createdElements
|
||||
end
|
||||
|
||||
local function createHelpDisplay(typeOfHelp)
|
||||
local helpContents = nil
|
||||
local helpFrame = utility:Create'Frame'
|
||||
{
|
||||
Size = UDim2.new(1,0,1,0),
|
||||
BackgroundTransparency = 1,
|
||||
Name = "HelpFrame" .. tostring(typeOfHelp)
|
||||
};
|
||||
|
||||
|
||||
if typeOfHelp == KEYBOARD_MOUSE_TAG then
|
||||
createPCHelp(helpFrame)
|
||||
elseif typeOfHelp == GAMEPAD_TAG then
|
||||
createGamepadHelp(helpFrame)
|
||||
elseif typeOfHelp == TOUCH_TAG then
|
||||
helpContents = createTouchHelp(helpFrame)
|
||||
end
|
||||
|
||||
return helpFrame, helpContents
|
||||
end
|
||||
|
||||
local function displayHelp(currentPage)
|
||||
for i, helpPage in pairs(this.HelpPages) do
|
||||
if helpPage == currentPage then
|
||||
helpPage.Parent = this.Page
|
||||
this.Page.Size = helpPage.Size
|
||||
if isTenFootInterface then
|
||||
this.Page.Size = UDim2.new(helpPage.Size.X.Scale, helpPage.Size.X.Offset, helpPage.Size.Y.Scale, 0)
|
||||
end
|
||||
else
|
||||
helpPage.Parent = nil
|
||||
end
|
||||
end
|
||||
if UserInputService:GetPlatform() == Enum.Platform.XBoxOne then
|
||||
this.HubRef.PageViewClipper.ClipsDescendants = false
|
||||
this.HubRef.PageView.ClipsDescendants = false
|
||||
end
|
||||
end
|
||||
|
||||
local function switchToHelp(typeOfHelp)
|
||||
local helpPage = this.HelpPages[typeOfHelp]
|
||||
if helpPage then
|
||||
displayHelp(helpPage)
|
||||
if typeOfHelp == TOUCH_TAG then
|
||||
-- update for the control scheme
|
||||
local scheme = GameSettings.TouchMovementMode
|
||||
if this.ActiveHelpScheme ~= scheme then
|
||||
updateTouchLayout(scheme)
|
||||
end
|
||||
end
|
||||
else
|
||||
this.HelpPages[typeOfHelp], this.HelpPageContents[typeOfHelp] = createHelpDisplay(typeOfHelp)
|
||||
switchToHelp(typeOfHelp)
|
||||
end
|
||||
end
|
||||
|
||||
local function showTypeOfHelp()
|
||||
switchToHelp(this:GetCurrentInputType())
|
||||
end
|
||||
|
||||
local function adjustForScreenLayout(givenSize) -- portrait mode was causing the help frame to be either too tall or short when changed between landscape mode and portrait.
|
||||
if this:GetCurrentInputType() == TOUCH_TAG then
|
||||
local scheme = GameSettings.TouchMovementMode
|
||||
local smallScreen = utility:IsSmallTouchScreen()
|
||||
local size = givenSize or GuiService:GetScreenResolution()
|
||||
local ySize = size.y - 350
|
||||
if smallScreen then
|
||||
ySize = size.y - 100
|
||||
end
|
||||
this.HelpPages[TOUCH_TAG].Size = UDim2.new(1,0,0,ySize)
|
||||
updateTouchLayout(scheme)
|
||||
end
|
||||
end
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader.Name = "HelpTab"
|
||||
|
||||
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/HelpTab.png"
|
||||
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.Text = "Help"
|
||||
else
|
||||
this.TabHeader.Icon.Title.Text = "Help"
|
||||
end
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "Help"
|
||||
|
||||
UserInputService.InputBegan:connect(function(inputObject)
|
||||
local inputType = inputObject.UserInputType
|
||||
if inputType ~= Enum.UserInputType.Focus and inputType ~= Enum.UserInputType.None then
|
||||
lastInputType = inputObject.UserInputType
|
||||
showTypeOfHelp()
|
||||
end
|
||||
end)
|
||||
|
||||
utility:OnResized(this, function(newSize, isPortrait)
|
||||
if this.HelpPages[TOUCH_TAG] then
|
||||
adjustForScreenLayout(newSize)
|
||||
end
|
||||
end)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
do
|
||||
PageInstance = Initialize()
|
||||
|
||||
PageInstance.Displayed.Event:connect(function()
|
||||
local isPortrait = utility:IsPortrait()
|
||||
if PageInstance:GetCurrentInputType() == TOUCH_TAG then
|
||||
if PageInstance.HubRef.BottomButtonFrame and not utility:IsSmallTouchScreen() and not isPortrait then
|
||||
PageInstance.HubRef.BottomButtonFrame.Visible = false
|
||||
end
|
||||
end
|
||||
if PageInstance.HubRef.VersionContainer then
|
||||
PageInstance.HubRef.VersionContainer.Visible = true
|
||||
end
|
||||
end)
|
||||
|
||||
PageInstance.Hidden.Event:connect(function()
|
||||
PageInstance.HubRef.PageViewClipper.ClipsDescendants = true
|
||||
PageInstance.HubRef.PageView.ClipsDescendants = true
|
||||
|
||||
PageInstance.HubRef:ShowShield()
|
||||
|
||||
local isPortrait = utility:IsPortrait()
|
||||
if PageInstance:GetCurrentInputType() == TOUCH_TAG then
|
||||
if PageInstance.HubRef.BottomButtonFrame and not utility:IsSmallTouchScreen() and not isPortrait then
|
||||
PageInstance.HubRef.BottomButtonFrame.Visible = true
|
||||
end
|
||||
end
|
||||
if PageInstance.HubRef.VersionContainer then
|
||||
PageInstance.HubRef.VersionContainer.Visible = false
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
return PageInstance
|
||||
@@ -0,0 +1,90 @@
|
||||
--[[
|
||||
Filename: Home.lua
|
||||
Written by: jeditkacheff
|
||||
Version 1.0
|
||||
Description: Takes care of the home page in Settings Menu
|
||||
--]]
|
||||
|
||||
local BUTTON_OFFSET = 20
|
||||
local BUTTON_SPACING = 10
|
||||
|
||||
-------------- SERVICES --------------
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
|
||||
----------- UTILITIES --------------
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
|
||||
------------ Variables -------------------
|
||||
local PageInstance = nil
|
||||
|
||||
local success, result = pcall(function() return settings():GetFFlag('UseNotificationsLocalization') end)
|
||||
local FFlagUseNotificationsLocalization = success and result
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
|
||||
local function Initialize()
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader.Name = "HomeTab"
|
||||
|
||||
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/HomeTab.png"
|
||||
this.TabHeader.Icon.Size = UDim2.new(0,32,0,30)
|
||||
this.TabHeader.Icon.Position = UDim2.new(0,5,0.5,-15)
|
||||
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.Text = "Home"
|
||||
else
|
||||
this.TabHeader.Icon.Title.Text = "Home"
|
||||
end
|
||||
|
||||
this.TabHeader.Size = UDim2.new(0,100,1,0)
|
||||
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "Home"
|
||||
local resumeGameFunc = function()
|
||||
this.HubRef:SetVisibility(false)
|
||||
end
|
||||
|
||||
this.ResumeButton = utility:MakeStyledButton("ResumeButton", "Resume Game", UDim2.new(0, 200, 0, 50), resumeGameFunc)
|
||||
this.ResumeButton.Position = UDim2.new(0.5,-100,0,BUTTON_OFFSET)
|
||||
this.ResumeButton.Parent = this.Page
|
||||
|
||||
local resetFunc = function()
|
||||
this.HubRef:SwitchToPage(this.HubRef.ResetCharacterPage, false, 1)
|
||||
end
|
||||
|
||||
local resetButton = utility:MakeStyledButton("ResetButton", "Reset Character", UDim2.new(0, 200, 0, 50), resetFunc)
|
||||
resetButton.Position = UDim2.new(0.5,-100,0,this.ResumeButton.AbsolutePosition.Y + this.ResumeButton.AbsoluteSize.Y + BUTTON_SPACING)
|
||||
resetButton.Parent = this.Page
|
||||
|
||||
local leaveGameFunc = function()
|
||||
this.HubRef:SwitchToPage(this.HubRef.LeaveGamePage, false, 1)
|
||||
end
|
||||
|
||||
local leaveButton = utility:MakeStyledButton("LeaveButton", "Leave Game", UDim2.new(0, 200, 0, 50), leaveGameFunc)
|
||||
leaveButton.Position = UDim2.new(0.5,-100,0,resetButton.AbsolutePosition.Y + resetButton.AbsoluteSize.Y + BUTTON_SPACING)
|
||||
leaveButton.Parent = this.Page
|
||||
|
||||
this.Page.Size = UDim2.new(1,0,0,leaveButton.AbsolutePosition.Y + leaveButton.AbsoluteSize.Y)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
do
|
||||
PageInstance = Initialize()
|
||||
|
||||
PageInstance.Displayed.Event:connect(function()
|
||||
if not utility:UsesSelectedObject() then return end
|
||||
|
||||
GuiService.SelectedCoreObject = PageInstance.ResumeButton
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
return PageInstance
|
||||
@@ -0,0 +1,142 @@
|
||||
--[[
|
||||
Filename: LeaveGame.lua
|
||||
Written by: jeditkacheff
|
||||
Version 1.0
|
||||
Description: Takes care of the leave game in Settings Menu
|
||||
--]]
|
||||
|
||||
|
||||
-------------- CONSTANTS -------------
|
||||
local LEAVE_GAME_ACTION = "LeaveGameCancelAction"
|
||||
local LEAVE_GAME_FRAME_WAITS = 2
|
||||
|
||||
-------------- SERVICES --------------
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
----------- UTILITIES --------------
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
|
||||
------------ Variables -------------------
|
||||
local PageInstance = nil
|
||||
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
|
||||
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
|
||||
local function Initialize()
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
|
||||
this.LeaveFunc = function()
|
||||
GuiService.SelectedCoreObject = nil -- deselects the button and prevents spamming the popup to save in studio when using gamepad
|
||||
|
||||
-- need to wait for render frames so on slower devices the leave button highlight will update
|
||||
-- otherwise, since on slow devices it takes so long to leave you are left wondering if you pressed the button
|
||||
for i = 1, LEAVE_GAME_FRAME_WAITS do
|
||||
RunService.RenderStepped:wait()
|
||||
end
|
||||
|
||||
game:Shutdown()
|
||||
end
|
||||
this.DontLeaveFunc = function(isUsingGamepad)
|
||||
if this.HubRef then
|
||||
this.HubRef:PopMenu(isUsingGamepad, true)
|
||||
end
|
||||
end
|
||||
this.DontLeaveFromHotkey = function(name, state, input)
|
||||
if state == Enum.UserInputState.Begin then
|
||||
local isUsingGamepad = input.UserInputType == Enum.UserInputType.Gamepad1 or input.UserInputType == Enum.UserInputType.Gamepad2
|
||||
or input.UserInputType == Enum.UserInputType.Gamepad3 or input.UserInputType == Enum.UserInputType.Gamepad4
|
||||
|
||||
this.DontLeaveFunc(isUsingGamepad)
|
||||
end
|
||||
end
|
||||
this.DontLeaveFromButton = function(isUsingGamepad)
|
||||
this.DontLeaveFunc(isUsingGamepad)
|
||||
end
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader = nil -- no tab for this page
|
||||
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "LeaveGamePage"
|
||||
this.ShouldShowBottomBar = false
|
||||
this.ShouldShowHubBar = false
|
||||
|
||||
local leaveGameText = utility:Create'TextLabel'
|
||||
{
|
||||
Name = "LeaveGameText",
|
||||
Text = "Are you sure you want to leave the game?",
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size36,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1,0,0,200),
|
||||
TextWrapped = true,
|
||||
ZIndex = 2,
|
||||
Parent = this.Page,
|
||||
Position = isTenFootInterface and UDim2.new(0,0,0,100) or UDim2.new(0,0,0,0)
|
||||
};
|
||||
|
||||
local leaveButtonContainer = utility:Create"Frame"
|
||||
{
|
||||
Name = "LeaveButtonContainer",
|
||||
Parent = leaveGameText,
|
||||
Size = UDim2.new(1,0,0,400),
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0,0,1,0)
|
||||
};
|
||||
|
||||
local leaveButtonLayout = utility:Create'UIGridLayout'
|
||||
{
|
||||
Name = "LeavetButtonsLayout",
|
||||
CellSize = isTenFootInterface and UDim2.new(0, 300, 0, 80) or UDim2.new(0, 200, 0, 50),
|
||||
CellPadding = UDim2.new(0,20,0,20),
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
Parent = leaveButtonContainer
|
||||
};
|
||||
|
||||
if utility:IsSmallTouchScreen() then
|
||||
leaveGameText.FontSize = Enum.FontSize.Size24
|
||||
leaveGameText.Size = UDim2.new(1,0,0,100)
|
||||
elseif isTenFootInterface then
|
||||
leaveGameText.FontSize = Enum.FontSize.Size48
|
||||
end
|
||||
|
||||
this.LeaveGameButton = utility:MakeStyledButton("LeaveGame", "Leave", nil, this.LeaveFunc)
|
||||
this.LeaveGameButton.NextSelectionRight = nil
|
||||
this.LeaveGameButton.Parent = leaveButtonContainer
|
||||
|
||||
------------- Init ----------------------------------
|
||||
|
||||
local dontleaveGameButton = utility:MakeStyledButton("DontLeaveGame", "Don't Leave", nil, this.DontLeaveFromButton)
|
||||
dontleaveGameButton.NextSelectionLeft = nil
|
||||
dontleaveGameButton.Parent = leaveButtonContainer
|
||||
|
||||
this.Page.Size = UDim2.new(1,0,0,dontleaveGameButton.AbsolutePosition.Y + dontleaveGameButton.AbsoluteSize.Y)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
PageInstance = Initialize()
|
||||
|
||||
PageInstance.Displayed.Event:connect(function()
|
||||
GuiService.SelectedCoreObject = PageInstance.LeaveGameButton
|
||||
ContextActionService:BindCoreAction(LEAVE_GAME_ACTION, PageInstance.DontLeaveFromHotkey, false, Enum.KeyCode.ButtonB)
|
||||
end)
|
||||
|
||||
PageInstance.Hidden.Event:connect(function()
|
||||
ContextActionService:UnbindCoreAction(LEAVE_GAME_ACTION)
|
||||
end)
|
||||
|
||||
|
||||
return PageInstance
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
local Constants = {
|
||||
Color = {
|
||||
BLUE = Color3.fromRGB(0, 162, 255),
|
||||
GREY = Color3.fromRGB(155, 155, 155),
|
||||
WHITE = Color3.fromRGB(255, 255, 255),
|
||||
DARK = Color3.fromRGB(25, 25, 25),
|
||||
},
|
||||
|
||||
TextSize = {
|
||||
BUTTON = 36
|
||||
},
|
||||
|
||||
Image = {
|
||||
BUTTON = "rbxasset://textures/ui/Settings/LeaveGame/Button_1080.png",
|
||||
BUTTON_SELECTOR = "rbxasset://textures/ui/Settings/LeaveGame/gr-item selector-8px corner.png",
|
||||
PLAY_NEXT_SELECTOR = "rbxasset://textures/ui/Settings/LeaveGame/selectorWithIcon.png",
|
||||
DOWN_ARROW = "rbxasset://textures/ui/Settings/LeaveGame/artAssets_DownArrow.png",
|
||||
THUMB_ICON = "rbxasset://textures/ui/Settings/LeaveGame/thumb_strokeStyle.png",
|
||||
PLAYER_NUMBER_ICON = "rbxasset://textures/ui/Settings/LeaveGame/playernumber_strokeStyle.png",
|
||||
},
|
||||
}
|
||||
|
||||
return Constants
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
--[[
|
||||
A Roact play next game screen.
|
||||
Props:
|
||||
screenId : Variant
|
||||
zIndex : number
|
||||
ref : Roact.Ref
|
||||
focused : bool
|
||||
closeScreen : function()
|
||||
numOfGames : number
|
||||
]]
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local HttpService = game:GetService("HttpService")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Otter = require(CorePackages.Otter)
|
||||
|
||||
local Settings = script.Parent.Parent.Parent
|
||||
local Modules = Settings.Parent
|
||||
local Common = Modules.Common
|
||||
local Analytics = require(Common.Analytics)
|
||||
local Constants = require(Settings.Pages.LeaveGameScreen.Constants)
|
||||
local ResumeButton = require(Settings.Components.ResumeButton)
|
||||
local LeaveButton = require(Settings.Components.LeaveButton)
|
||||
local Utility = require(Settings.Utility)
|
||||
local RobloxTranslator = require(Modules.RobloxTranslator)
|
||||
|
||||
--NOTE: This play next game view can be swaped different version such as one for touch and PC
|
||||
local PlayNextGameView = require(Settings.Components.PlayNextGame.PlayNextGameXbox)
|
||||
|
||||
local ApiFetchRecentlyPlayedGames = require(Settings.Thunks.ApiFetchRecentlyPlayedGames)
|
||||
|
||||
local LEAVE_BUTTON_KEY = "LeaveButton"
|
||||
local DONT_LEAVE_BUTTON_KEY = "DontLeaveButton"
|
||||
local NEXT_GAME_LIST = "NextGameList"
|
||||
local LEAVE_GAME_LABEL_KEY = "Feature.SettingsHub.Label.LeaveGame"
|
||||
|
||||
local LeaveGame = Roact.PureComponent:extend("LeaveGame")
|
||||
|
||||
local LEAVE_GAME_ALERT_SHOW_Y = 267
|
||||
local LEAVE_GAME_ALERT_HIDE_Y = 183
|
||||
|
||||
-- NOTE: event context might need to be changed for other platforms.
|
||||
local EVENT_CONTEXT = "XboxOne"
|
||||
function LeaveGame:init()
|
||||
self.screenId = self.props.screenId or HttpService:GenerateGUID(false)
|
||||
self.ref = Roact.createRef()
|
||||
self.analytics = Analytics.new()
|
||||
self.motorOptions = {
|
||||
dampingRatio = 1,
|
||||
frequency = 5,
|
||||
}
|
||||
self.leaveButtonRef = Roact.createRef()
|
||||
self.resume = function()
|
||||
self.props.closeScreen()
|
||||
end
|
||||
self.enterNextGameList = function()
|
||||
if #self.props.gamesList == 0 then
|
||||
self:_selectButton()
|
||||
return
|
||||
end
|
||||
local eventName = "EnterNextGameList"
|
||||
self.analytics.EventStream:setRBXEventStream(EVENT_CONTEXT, eventName)
|
||||
self:setState({
|
||||
enterGameList = true
|
||||
})
|
||||
end
|
||||
|
||||
self.leaveNextGameList = function()
|
||||
self:setState({
|
||||
enterGameList = false
|
||||
})
|
||||
end
|
||||
self.state = {
|
||||
enterGameList = false,
|
||||
}
|
||||
end
|
||||
|
||||
function LeaveGame:willUpdate(nextProps, nextState)
|
||||
assert(self.props.screenId == nextProps.screenId)
|
||||
end
|
||||
|
||||
function LeaveGame:render()
|
||||
local zIndex = self.props.zIndex or 1
|
||||
local leaveGameAlert = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(0, 672, 0, 167),
|
||||
Position = UDim2.new(0.5, 0, 0, LEAVE_GAME_ALERT_SHOW_Y),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
[Roact.Ref] = self.ref,
|
||||
},{
|
||||
LeaveGameText = Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(1, 0, 0, 45),
|
||||
Position = UDim2.new(0.5, 0, 0, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Text = RobloxTranslator:FormatByKey(LEAVE_GAME_LABEL_KEY),
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size36,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
BackgroundTransparency = 1,
|
||||
TextWrapped = true,
|
||||
ZIndex = zIndex + 1,
|
||||
}),
|
||||
[LEAVE_BUTTON_KEY] = Roact.createElement(LeaveButton, {
|
||||
position = UDim2.new(0, 0, 1, 0),
|
||||
anchorPoint = Vector2.new(0, 1),
|
||||
objRef = self.leaveButtonRef,
|
||||
zIndex = zIndex,
|
||||
}),
|
||||
[DONT_LEAVE_BUTTON_KEY] = Roact.createElement(ResumeButton, {
|
||||
position = UDim2.new(1, 0, 1, 0),
|
||||
anchorPoint = Vector2.new(1, 1),
|
||||
onResume = self.resume,
|
||||
zIndex = zIndex,
|
||||
}),
|
||||
Redirect = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1.5, 0, 0, 1),
|
||||
Position = UDim2.new(0.5, 0, 1, 100),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Selectable = true,
|
||||
BackgroundTransparency = 1,
|
||||
[Roact.Event.SelectionGained] = function()
|
||||
GuiService.SelectedCoreObject = nil
|
||||
self.enterNextGameList()
|
||||
end
|
||||
}),
|
||||
})
|
||||
local playNextGameView
|
||||
if #self.props.gamesList ~= 0 then
|
||||
playNextGameView = Roact.createElement(PlayNextGameView, {
|
||||
screenId = NEXT_GAME_LIST,
|
||||
focused = self.state.enterGameList,
|
||||
onLeaveNextGameList = self.leaveNextGameList,
|
||||
gamesData = self.props.gamesList,
|
||||
zIndex = zIndex,
|
||||
})
|
||||
end
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundColor3 = Color3.fromRGB(25,25,25),
|
||||
BackgroundTransparency = 0.7,
|
||||
BorderSizePixel = 0,
|
||||
ZIndex = zIndex,
|
||||
},{
|
||||
LeaveGameAlert = leaveGameAlert,
|
||||
PlayNextGameView = playNextGameView,
|
||||
})
|
||||
end
|
||||
|
||||
function LeaveGame:_selectButton()
|
||||
if self.props.focused then
|
||||
local buttonRef = self.leaveButtonRef
|
||||
local buttonObj = buttonRef and buttonRef.current or nil
|
||||
if buttonObj ~= nil then
|
||||
GuiService.SelectedCoreObject = buttonObj
|
||||
end
|
||||
end
|
||||
end
|
||||
function LeaveGame:didMount()
|
||||
if #self.props.gamesList ~= self.props.numOfGames then
|
||||
self.props.GetRecentlyPlayedGames(self.props.numOfGames, true)
|
||||
end
|
||||
delay(0, function()
|
||||
if self.props.focused and self.ref.current then
|
||||
GuiService:AddSelectionParent(self.screenId, self.ref.current)
|
||||
end
|
||||
self:_selectButton()
|
||||
end)
|
||||
self.motor = Otter.createSingleMotor(0)
|
||||
self.motor:onStep(function(value)
|
||||
local t = value/100
|
||||
if self.ref.current ~= nil then
|
||||
self.ref.current.Position = UDim2.new(0.5, 0, 0, Utility:Round(Utility:Lerp(t, LEAVE_GAME_ALERT_SHOW_Y, LEAVE_GAME_ALERT_HIDE_Y)))
|
||||
end
|
||||
end)
|
||||
self.motor:start()
|
||||
local eventName = "OpenedLeaveGameScreen"
|
||||
self.analytics.EventStream:setRBXEventStream(EVENT_CONTEXT, eventName)
|
||||
end
|
||||
|
||||
function LeaveGame:didUpdate(previousProps, previousState)
|
||||
local targetPercentage = self.state.enterGameList and 100 or 0
|
||||
self.motor:setGoal(Otter.spring(targetPercentage, self.motorOptions))
|
||||
|
||||
if self.state.enterGameList == true and
|
||||
self.props.focused == previousProps.focused then
|
||||
return
|
||||
end
|
||||
GuiService:RemoveSelectionGroup(self.screenId)
|
||||
if self.props.focused and self.ref.current then
|
||||
GuiService:AddSelectionParent(self.screenId, self.ref.current)
|
||||
end
|
||||
self:_selectButton()
|
||||
end
|
||||
|
||||
function LeaveGame:willUnmount()
|
||||
GuiService:RemoveSelectionGroup(self.screenId)
|
||||
self.motor:destroy()
|
||||
end
|
||||
|
||||
local function mapStateToProps(state, props)
|
||||
local currentUniverseId = game.GameId
|
||||
if state.RecentlyPlayedGamesFetchingStatus.fetching ~= false then
|
||||
return {
|
||||
gamesList = {}
|
||||
}
|
||||
end
|
||||
local recentlyPlayedGamesState = state.RecentlyPlayedGames
|
||||
local gameSort = recentlyPlayedGamesState.gameSort or {}
|
||||
--Fliter our the current game if it is in the sort
|
||||
local filteredSort = {}
|
||||
for i in ipairs(gameSort) do
|
||||
if gameSort[i].universeId ~= currentUniverseId then
|
||||
table.insert(filteredSort, gameSort[i])
|
||||
end
|
||||
end
|
||||
return {
|
||||
gamesList = filteredSort,
|
||||
}
|
||||
end
|
||||
local function mapDispatchToProps(dispatch)
|
||||
return
|
||||
{
|
||||
GetRecentlyPlayedGames = function(numOfGames, forceUpdate)
|
||||
return dispatch(ApiFetchRecentlyPlayedGames(numOfGames, forceUpdate))
|
||||
end,
|
||||
}
|
||||
end
|
||||
return RoactRodux.UNSTABLE_connect2(mapStateToProps, mapDispatchToProps)(LeaveGame)
|
||||
@@ -0,0 +1,81 @@
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Settings = script.Parent.Parent
|
||||
local SettingsState = require(Settings.SettingsState)
|
||||
local SettingsPageFactory = require(Settings.SettingsPageFactory)
|
||||
local ApiFetchRecentlyPlayedGames = require(Settings.Thunks.ApiFetchRecentlyPlayedGames)
|
||||
local LeaveGameScreen = require(Settings.Pages.LeaveGameScreen.LeaveGame)
|
||||
|
||||
-------------- CONSTANTS -------------
|
||||
local LEAVE_GAME_ACTION = "LeaveGameCancelAction"
|
||||
-- NOTE: The current game will be filtered out.
|
||||
local NUM_OF_GAMES = 7
|
||||
|
||||
local PageInstance
|
||||
|
||||
local function Initialize()
|
||||
local this = SettingsPageFactory:CreateNewPage()
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader = nil -- no tab for this page
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader = nil -- no tab for this page
|
||||
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "LeaveGamePage"
|
||||
this.ShouldShowBottomBar = false
|
||||
this.ShouldShowHubBar = false
|
||||
this.IsPageClipped = false
|
||||
|
||||
SettingsState.store:dispatch(ApiFetchRecentlyPlayedGames(NUM_OF_GAMES))
|
||||
this.unMountFunction = function(isUsingGamepad)
|
||||
if this.HubRef then
|
||||
this.HubRef:PopMenu(isUsingGamepad, true)
|
||||
end
|
||||
end
|
||||
this.unMountFromHotkey = function(name, state, input)
|
||||
if state == Enum.UserInputState.Begin then
|
||||
local isUsingGamepad = input.UserInputType == Enum.UserInputType.Gamepad1 or input.UserInputType == Enum.UserInputType.Gamepad2
|
||||
or input.UserInputType == Enum.UserInputType.Gamepad3 or input.UserInputType == Enum.UserInputType.Gamepad4
|
||||
|
||||
this.unMountFunction(isUsingGamepad)
|
||||
end
|
||||
end
|
||||
this.RoactScreen = Roact.createElement(RoactRodux.StoreProvider,
|
||||
{
|
||||
store = SettingsState.store,
|
||||
},{
|
||||
Screen = Roact.createElement("ScreenGui", {}, {
|
||||
LeaveGameScreen = Roact.createElement(LeaveGameScreen, {
|
||||
focused = true,
|
||||
closeScreen = this.unMountFunction,
|
||||
zIndex = 2,
|
||||
numOfGames = NUM_OF_GAMES,
|
||||
}),
|
||||
})
|
||||
})
|
||||
return this
|
||||
end
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
PageInstance = Initialize()
|
||||
|
||||
PageInstance.Displayed.Event:connect(function()
|
||||
PageInstance.handle = Roact.mount(PageInstance.RoactScreen, CoreGui)
|
||||
ContextActionService:BindCoreAction(LEAVE_GAME_ACTION, PageInstance.unMountFromHotkey, false, Enum.KeyCode.ButtonB)
|
||||
end)
|
||||
|
||||
PageInstance.Hidden.Event:connect(function()
|
||||
ContextActionService:UnbindCoreAction(LEAVE_GAME_ACTION)
|
||||
if PageInstance.handle ~= nil then
|
||||
Roact.unmount(PageInstance.handle)
|
||||
PageInstance.handle = nil
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
return PageInstance
|
||||
@@ -0,0 +1,675 @@
|
||||
--[[
|
||||
Filename: Players.lua
|
||||
Written by: Stickmasterluke
|
||||
Version 1.0
|
||||
Description: Player list inside escape menu, with friend adding functionality.
|
||||
--]]
|
||||
-------------- SERVICES --------------
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local PlayersService = game:GetService("Players")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
local AnalyticsService = game:GetService("AnalyticsService")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
----------- UTILITIES --------------
|
||||
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
local reportAbuseMenu = require(RobloxGui.Modules.Settings.Pages.ReportAbuseMenu)
|
||||
local SocialUtil = require(RobloxGui.Modules:WaitForChild("SocialUtil"))
|
||||
local EventStream = require(CorePackages.AppTempCommon.Temp.EventStream)
|
||||
local FlagSettings = require(CoreGui.RobloxGui.Modules.Settings.Pages.ShareGame.FlagSettings)
|
||||
local ShareGameIcons = require(CoreGui.RobloxGui.Modules.Settings.Pages.ShareGame.Spritesheets.ShareGameIcons)
|
||||
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
------------ Constants -------------------
|
||||
local FRAME_DEFAULT_TRANSPARENCY = .85
|
||||
local FRAME_SELECTED_TRANSPARENCY = .65
|
||||
local REPORT_PLAYER_IMAGE = isTenFootInterface and "rbxasset://textures/ui/Settings/Players/ReportFlagIcon@2x.png" or "rbxasset://textures/ui/Settings/Players/ReportFlagIcon.png"
|
||||
local ADD_FRIEND_IMAGE = isTenFootInterface and "rbxasset://textures/ui/Settings/Players/AddFriendIcon@2x.png" or "rbxasset://textures/ui/Settings/Players/AddFriendIcon.png"
|
||||
local FRIEND_IMAGE = isTenFootInterface and "rbxasset://textures/ui/Settings/Players/FriendIcon@2x.png" or "rbxasset://textures/ui/Settings/Players/FriendIcon.png"
|
||||
|
||||
local PLAYER_ROW_HEIGHT = 62
|
||||
local PLAYER_ROW_SPACING = 80
|
||||
|
||||
------------ Variables -------------------
|
||||
local platform = UserInputService:GetPlatform()
|
||||
local PageInstance = nil
|
||||
local localPlayer = PlayersService.LocalPlayer
|
||||
while not localPlayer do
|
||||
PlayersService.ChildAdded:wait()
|
||||
localPlayer = PlayersService.LocalPlayer
|
||||
end
|
||||
|
||||
|
||||
------------ FAST FLAGS -------------------
|
||||
local success, result = pcall(function() return settings():GetFFlag('UseNotificationsLocalization') end)
|
||||
local FFlagUseNotificationsLocalization = success and result
|
||||
|
||||
local IsShareGamePageEnabledByPlatform = FlagSettings.IsShareGamePageEnabledByPlatform(platform)
|
||||
local FFlagSettingsHubInviteToGameInStudio = settings():GetFFlag('SettingsHubInviteToGameInStudio4')
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
local function Initialize()
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
|
||||
this.PageListLayout.Padding = UDim.new(0, PLAYER_ROW_SPACING - PLAYER_ROW_HEIGHT)
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader.Name = "PlayersTab"
|
||||
this.TabHeader.Icon.Image = isTenFootInterface and "rbxasset://textures/ui/Settings/MenuBarIcons/PlayersTabIcon@2x.png" or "rbxasset://textures/ui/Settings/MenuBarIcons/PlayersTabIcon.png"
|
||||
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.Text = "Players"
|
||||
else
|
||||
this.TabHeader.Icon.Title.Text = "Players"
|
||||
end
|
||||
|
||||
----- FRIENDSHIP FUNCTIONS ------
|
||||
local function getFriendStatus(selectedPlayer)
|
||||
local success, result = pcall(function()
|
||||
-- NOTE: Core script only
|
||||
return localPlayer:GetFriendStatus(selectedPlayer)
|
||||
end)
|
||||
if success then
|
||||
return result
|
||||
else
|
||||
return Enum.FriendStatus.NotFriend
|
||||
end
|
||||
end
|
||||
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "Players"
|
||||
|
||||
local function showRightSideButtons(player)
|
||||
return player and player ~= localPlayer and player.UserId > 0 and localPlayer.UserId > 0
|
||||
end
|
||||
|
||||
local function createFriendStatusTextLabel(status, player)
|
||||
if status == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local fakeSelection = Instance.new("Frame")
|
||||
fakeSelection.BackgroundTransparency = 1
|
||||
|
||||
local friendLabel = nil
|
||||
local friendLabelText = nil
|
||||
if status == Enum.FriendStatus.Friend or status == Enum.FriendStatus.FriendRequestSent then
|
||||
friendLabel = Instance.new("TextButton")
|
||||
friendLabel.BackgroundTransparency = 1
|
||||
friendLabel.FontSize = Enum.FontSize.Size24
|
||||
friendLabel.Font = Enum.Font.SourceSans
|
||||
friendLabel.TextColor3 = Color3.new(1,1,1)
|
||||
friendLabel.SelectionImageObject = fakeSelection
|
||||
if status == Enum.FriendStatus.Friend then
|
||||
friendLabel.Text = "Friend"
|
||||
else
|
||||
friendLabel.Text = "Request Sent"
|
||||
end
|
||||
elseif status == Enum.FriendStatus.Unknown or status == Enum.FriendStatus.NotFriend or status == Enum.FriendStatus.FriendRequestReceived then
|
||||
local addFriendFunc = function()
|
||||
if friendLabel and friendLabelText and friendLabelText.Text ~= "" then
|
||||
friendLabel.ImageTransparency = 1
|
||||
friendLabelText.Text = ""
|
||||
if localPlayer and player then
|
||||
AnalyticsService:ReportCounter("PlayersMenu-RequestFriendship")
|
||||
AnalyticsService:TrackEvent("Game", "RequestFriendship", "PlayersMenu")
|
||||
|
||||
localPlayer:RequestFriendship(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
friendLabel, friendLabelText = utility:MakeStyledButton("FriendStatus", "Add Friend", UDim2.new(0, 182, 0, 46), addFriendFunc)
|
||||
friendLabelText.ZIndex = 3
|
||||
friendLabelText.Position = friendLabelText.Position + UDim2.new(0,0,0,1)
|
||||
end
|
||||
|
||||
if friendLabel then
|
||||
friendLabel.Name = "FriendStatus"
|
||||
friendLabel.Size = UDim2.new(0,182,0,46)
|
||||
friendLabel.Position = UDim2.new(1,-198,0,7)
|
||||
friendLabel.ZIndex = 3
|
||||
end
|
||||
return friendLabel
|
||||
end
|
||||
|
||||
local function createFriendStatusImageLabel(status, player)
|
||||
if status == Enum.FriendStatus.Friend or status == Enum.FriendStatus.FriendRequestSent then
|
||||
local friendLabel = Instance.new("ImageButton")
|
||||
friendLabel.Name = "FriendStatus"
|
||||
friendLabel.Size = UDim2.new(0, 46, 0, 46)
|
||||
friendLabel.Image = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButton.png"
|
||||
friendLabel.ScaleType = Enum.ScaleType.Slice
|
||||
friendLabel.SliceCenter = Rect.new(8,6,46,44)
|
||||
friendLabel.AutoButtonColor = false
|
||||
friendLabel.BackgroundTransparency = 1
|
||||
friendLabel.ZIndex = 2
|
||||
local friendImage = Instance.new("ImageLabel")
|
||||
friendImage.BackgroundTransparency = 1
|
||||
friendImage.Position = UDim2.new(0.5, 0, 0.5, 0)
|
||||
friendImage.Size = UDim2.new(0, 28, 0, 28)
|
||||
friendImage.AnchorPoint = Vector2.new(0.5, 0.5)
|
||||
friendImage.ZIndex = 3
|
||||
friendImage.Image = FRIEND_IMAGE
|
||||
if status == Enum.FriendStatus.Friend then
|
||||
friendImage.ImageTransparency = 0
|
||||
else
|
||||
friendImage.Image = ADD_FRIEND_IMAGE
|
||||
friendImage.ImageTransparency = 0.5
|
||||
end
|
||||
friendImage.Parent = friendLabel
|
||||
return friendLabel
|
||||
elseif status == Enum.FriendStatus.Unknown or status == Enum.FriendStatus.NotFriend or status == Enum.FriendStatus.FriendRequestReceived then
|
||||
local addFriendButton, addFriendImage = nil
|
||||
local addFriendFunc = function()
|
||||
if addFriendButton and addFriendImage and addFriendButton.ImageTransparency ~= 1 then
|
||||
addFriendButton.ImageTransparency = 1
|
||||
addFriendImage.ImageTransparency = 1
|
||||
if localPlayer and player then
|
||||
AnalyticsService:ReportCounter("PlayersMenu-RequestFriendship")
|
||||
AnalyticsService:TrackEvent("Game", "RequestFriendship", "PlayersMenu")
|
||||
|
||||
localPlayer:RequestFriendship(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
addFriendButton, addFriendImage = utility:MakeStyledImageButton("FriendStatus", ADD_FRIEND_IMAGE,
|
||||
UDim2.new(0, 46, 0, 46), UDim2.new(0, 28, 0, 28), addFriendFunc)
|
||||
addFriendButton.Name = "FriendStatus"
|
||||
addFriendButton.Selectable = true
|
||||
return addFriendButton
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Ideally we want to select the first add friend button, but select the first report button instead if none are available.
|
||||
local reportSelectionFound = nil
|
||||
local friendSelectionFound = nil
|
||||
local function friendStatusCreate(playerLabel, player)
|
||||
local friendLabelParent = nil
|
||||
if playerLabel then
|
||||
friendLabelParent = playerLabel:FindFirstChild("RightSideButtons")
|
||||
end
|
||||
|
||||
if friendLabelParent then
|
||||
-- remove any previous friend status labels
|
||||
for _, item in pairs(friendLabelParent:GetChildren()) do
|
||||
if item and item.Name == "FriendStatus" then
|
||||
if GuiService.SelectedCoreObject == item then
|
||||
friendSelectionFound = nil
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
item:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- create new friend status label
|
||||
local status = nil
|
||||
if showRightSideButtons(player) then
|
||||
status = getFriendStatus(player)
|
||||
end
|
||||
|
||||
local friendLabel = nil
|
||||
local wasIsPortrait = nil
|
||||
utility:OnResized(playerLabel, function(newSize, isPortrait)
|
||||
if friendLabel and isPortrait == wasIsPortrait then
|
||||
return
|
||||
end
|
||||
wasIsPortrait = isPortrait
|
||||
if friendLabel then
|
||||
friendLabel:Destroy()
|
||||
end
|
||||
if isPortrait then
|
||||
friendLabel = createFriendStatusImageLabel(status, player)
|
||||
else
|
||||
friendLabel = createFriendStatusTextLabel(status, player)
|
||||
end
|
||||
|
||||
if friendLabel then
|
||||
friendLabel.Name = "FriendStatus"
|
||||
friendLabel.LayoutOrder = 2
|
||||
friendLabel.Selectable = true
|
||||
friendLabel.Parent = friendLabelParent
|
||||
|
||||
if UserInputService.GamepadEnabled and not friendSelectionFound then
|
||||
friendSelectionFound = true
|
||||
GuiService.SelectedCoreObject = friendLabel
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
localPlayer.FriendStatusChanged:connect(function(player, friendStatus)
|
||||
if player then
|
||||
local playerLabel = this.Page:FindFirstChild("PlayerLabel"..player.Name)
|
||||
if playerLabel then
|
||||
friendStatusCreate(playerLabel, player)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local buttonsContainer = utility:Create("Frame") {
|
||||
Name = "ButtonsContainer",
|
||||
Size = UDim2.new(1, 0, 0, 62),
|
||||
BackgroundTransparency = 1,
|
||||
Parent = this.Page,
|
||||
|
||||
Visible = false
|
||||
}
|
||||
|
||||
local leaveGameFunc = function()
|
||||
this.HubRef:SwitchToPage(this.HubRef.LeaveGamePage, false, 1)
|
||||
end
|
||||
local leaveButton, leaveLabel = utility:MakeStyledButton("LeaveButton", "Leave Game", UDim2.new(1 / 3, -5, 1, 0), leaveGameFunc)
|
||||
leaveButton.AnchorPoint = Vector2.new(0, 0)
|
||||
leaveButton.Position = UDim2.new(0, 0, 0, 0)
|
||||
leaveLabel.Size = UDim2.new(1, 0, 1, -6)
|
||||
leaveButton.Parent = buttonsContainer
|
||||
|
||||
local resetFunc = function()
|
||||
this.HubRef:SwitchToPage(this.HubRef.ResetCharacterPage, false, 1)
|
||||
end
|
||||
local resetButton, resetLabel = utility:MakeStyledButton("ResetButton", "Reset Character", UDim2.new(1 / 3, -5, 1, 0), resetFunc)
|
||||
resetButton.AnchorPoint = Vector2.new(0.5, 0)
|
||||
resetButton.Position = UDim2.new(0.5, 0, 0, 0)
|
||||
resetLabel.Size = UDim2.new(1, 0, 1, -6)
|
||||
resetButton.Parent = buttonsContainer
|
||||
|
||||
local resumeGameFunc = function()
|
||||
this.HubRef:SetVisibility(false)
|
||||
end
|
||||
local resumeButton, resumeLabel = utility:MakeStyledButton("ResumeButton", "Resume Game", UDim2.new(1 / 3, -5, 1, 0), resumeGameFunc)
|
||||
resumeButton.AnchorPoint = Vector2.new(1, 0)
|
||||
resumeButton.Position = UDim2.new(1, 0, 0, 0)
|
||||
resumeLabel.Size = UDim2.new(1, 0, 1, -6)
|
||||
resumeButton.Parent = buttonsContainer
|
||||
|
||||
utility:OnResized(buttonsContainer, function(newSize, isPortrait)
|
||||
if isPortrait or utility:IsSmallTouchScreen() then
|
||||
local buttonsFontSize = isPortrait and 18 or 24
|
||||
buttonsContainer.Visible = true
|
||||
buttonsContainer.Size = UDim2.new(1, 0, 0, isPortrait and 50 or 62)
|
||||
resetLabel.TextSize = buttonsFontSize
|
||||
leaveLabel.TextSize = buttonsFontSize
|
||||
resumeLabel.TextSize = buttonsFontSize
|
||||
else
|
||||
buttonsContainer.Visible = false
|
||||
buttonsContainer.Size = UDim2.new(1, 0, 0, 0)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
if FFlagUseNotificationsLocalization then
|
||||
local function ApplyLocalizeTextSettingsToLabel(label)
|
||||
label.AnchorPoint = Vector2.new(0.5,0.5)
|
||||
label.Position = UDim2.new(0.5, 0, 0.5, -3)
|
||||
label.Size = UDim2.new(0.75, 0, 0.5, 0)
|
||||
end
|
||||
ApplyLocalizeTextSettingsToLabel(leaveLabel)
|
||||
ApplyLocalizeTextSettingsToLabel(resetLabel)
|
||||
ApplyLocalizeTextSettingsToLabel(resetLabel)
|
||||
end
|
||||
|
||||
local function reportAbuseButtonCreate(playerLabel, player)
|
||||
local rightSideButtons = playerLabel:FindFirstChild("RightSideButtons")
|
||||
if rightSideButtons then
|
||||
local oldReportButton = rightSideButtons:FindFirstChild("ReportPlayer")
|
||||
if oldReportButton then
|
||||
if oldReportButton == GuiService.SelectedCoreObject then
|
||||
reportSelectionFound = nil
|
||||
end
|
||||
oldReportButton:Destroy()
|
||||
end
|
||||
|
||||
if showRightSideButtons(player) then
|
||||
local reportPlayerFunction = function()
|
||||
reportAbuseMenu:ReportPlayer(player)
|
||||
end
|
||||
|
||||
local reportButton = utility:MakeStyledImageButton("ReportPlayer", REPORT_PLAYER_IMAGE,
|
||||
UDim2.new(0, 46, 0, 46), UDim2.new(0, 28, 0, 28), reportPlayerFunction)
|
||||
reportButton.Name = "ReportPlayer"
|
||||
reportButton.Position = UDim2.new(1, -260, 0, 7)
|
||||
reportButton.LayoutOrder = 1
|
||||
reportButton.Selectable = true
|
||||
reportButton.Parent = rightSideButtons
|
||||
|
||||
if not reportSelectionFound and not friendSelectionFound and UserInputService.GamepadEnabled then
|
||||
reportSelectionFound = true
|
||||
GuiService.SelectedCoreObject = reportButton
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local createShareGameButton = nil
|
||||
local createPlayerRow = nil
|
||||
|
||||
local function createRow(frameClassName)
|
||||
local frame = Instance.new(frameClassName)
|
||||
frame.Image = "rbxasset://textures/ui/dialog_white.png"
|
||||
frame.ScaleType = "Slice"
|
||||
frame.SliceCenter = Rect.new(10, 10, 10, 10)
|
||||
frame.Size = UDim2.new(1, 0, 0, PLAYER_ROW_HEIGHT)
|
||||
frame.Position = UDim2.new(0, 0, 0, 0)
|
||||
frame.BackgroundTransparency = 1
|
||||
frame.ZIndex = 2
|
||||
frame.ImageTransparency = FRAME_DEFAULT_TRANSPARENCY
|
||||
|
||||
local icon = Instance.new("ImageLabel")
|
||||
icon.Name = "Icon"
|
||||
icon.BackgroundTransparency = 1
|
||||
icon.Size = UDim2.new(0, 36, 0, 36)
|
||||
icon.Position = UDim2.new(0, 12, 0, 12)
|
||||
icon.ZIndex = 3
|
||||
icon.Parent = frame
|
||||
|
||||
local textLabel = Instance.new("TextLabel")
|
||||
textLabel.TextXAlignment = Enum.TextXAlignment.Left
|
||||
textLabel.Font = Enum.Font.SourceSans
|
||||
textLabel.FontSize = Enum.FontSize.Size24
|
||||
textLabel.TextColor3 = Color3.new(1, 1, 1)
|
||||
textLabel.BackgroundTransparency = 1
|
||||
textLabel.Position = UDim2.new(0, 60, .5, 0)
|
||||
textLabel.Size = UDim2.new(0, 0, 0, 0)
|
||||
textLabel.ZIndex = 3
|
||||
textLabel.Parent = frame
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
createShareGameButton = function()
|
||||
local frame = createRow("ImageButton")
|
||||
local textLabel = frame.TextLabel
|
||||
local icon = frame.Icon
|
||||
|
||||
textLabel.Font = Enum.Font.SourceSansSemibold
|
||||
textLabel.Text = RobloxTranslator:FormatByKey("Feature.SettingsHub.Action.InviteFriendsToPlay")
|
||||
|
||||
icon.Size = UDim2.new(0, 24, 0, 24)
|
||||
icon.Position = UDim2.new(0, 18, 0, 18)
|
||||
ShareGameIcons:ApplyImage(icon, "invite")
|
||||
|
||||
local function onHover(isHovering)
|
||||
if isHovering then
|
||||
frame.ImageTransparency = FRAME_SELECTED_TRANSPARENCY
|
||||
else
|
||||
frame.ImageTransparency = FRAME_DEFAULT_TRANSPARENCY
|
||||
end
|
||||
end
|
||||
|
||||
frame.InputBegan:Connect(function() onHover(true) end)
|
||||
frame.InputEnded:Connect(function() onHover(false) end)
|
||||
frame.Activated:Connect(function() onHover(false) end)
|
||||
frame.TouchPan:Connect(function(_, totalTranslation)
|
||||
local TAP_ACCURACY_THREASHOLD = 20
|
||||
if math.abs(totalTranslation.Y) > TAP_ACCURACY_THREASHOLD then
|
||||
onHover(false)
|
||||
end
|
||||
end)
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
createPlayerRow = function()
|
||||
local frame = createRow("ImageLabel")
|
||||
frame.TextLabel.Name = "NameLabel"
|
||||
|
||||
local rightSideButtons = Instance.new("Frame")
|
||||
rightSideButtons.Name = "RightSideButtons"
|
||||
rightSideButtons.BackgroundTransparency = 1
|
||||
rightSideButtons.ZIndex = 2
|
||||
rightSideButtons.Position = UDim2.new(0, 0, 0, 0)
|
||||
rightSideButtons.Size = UDim2.new(1, -10, 1, 0)
|
||||
rightSideButtons.Parent = frame
|
||||
|
||||
-- Selection Highlighting logic:
|
||||
local updateHighlight = function(lostSelectionObject)
|
||||
if frame then
|
||||
if GuiService.SelectedCoreObject and GuiService.SelectedCoreObject ~= lostSelectionObject and GuiService.SelectedCoreObject.Parent == rightSideButtons then
|
||||
frame.ImageTransparency = FRAME_SELECTED_TRANSPARENCY
|
||||
else
|
||||
frame.ImageTransparency = FRAME_DEFAULT_TRANSPARENCY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fakeSelectionObject = nil
|
||||
rightSideButtons.ChildAdded:connect(function(child)
|
||||
if child:IsA("GuiObject") then
|
||||
if fakeSelectionObject and child ~= fakeSelectionObject then
|
||||
fakeSelectionObject:Destroy()
|
||||
fakeSelectionObject = nil
|
||||
end
|
||||
child.SelectionGained:connect(function() updateHighlight(nil) end)
|
||||
child.SelectionLost:connect(function() updateHighlight(child) end)
|
||||
end
|
||||
end)
|
||||
|
||||
fakeSelectionObject = Instance.new("Frame")
|
||||
fakeSelectionObject.Selectable = true
|
||||
fakeSelectionObject.Size = UDim2.new(0, 100, 0, 100)
|
||||
fakeSelectionObject.BackgroundTransparency = 1
|
||||
fakeSelectionObject.SelectionImageObject = fakeSelectionObject:Clone()
|
||||
fakeSelectionObject.Parent = rightSideButtons
|
||||
|
||||
local rightSideListLayout = Instance.new("UIListLayout")
|
||||
rightSideListLayout.Name = "RightSideListLayout"
|
||||
rightSideListLayout.FillDirection = Enum.FillDirection.Horizontal
|
||||
rightSideListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Right
|
||||
rightSideListLayout.VerticalAlignment = Enum.VerticalAlignment.Center
|
||||
rightSideListLayout.SortOrder = Enum.SortOrder.LayoutOrder
|
||||
rightSideListLayout.Padding = UDim.new(0, 20)
|
||||
rightSideListLayout.Parent = rightSideButtons
|
||||
|
||||
pcall(function()
|
||||
frame.NameLabel.Localize = false
|
||||
end)
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
-- Manage cutting off a players name if it is too long when switching into portrait mode.
|
||||
local function managePlayerNameCutoff(frame, player)
|
||||
local wasIsPortrait = nil
|
||||
local reportFlagAddedConnection = nil
|
||||
local reportFlagChangedConnection = nil
|
||||
local function reportFlagChanged(reportFlag, prop)
|
||||
if prop == "AbsolutePosition" and wasIsPortrait then
|
||||
local maxPlayerNameSize = reportFlag.AbsolutePosition.X - 20 - frame.NameLabel.AbsolutePosition.X
|
||||
frame.NameLabel.Text = player.Name
|
||||
local newNameLength = string.len(player.Name)
|
||||
while frame.NameLabel.TextBounds.X > maxPlayerNameSize and newNameLength > 0 do
|
||||
frame.NameLabel.Text = string.sub(player.Name, 1, newNameLength) .. "..."
|
||||
newNameLength = newNameLength - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
utility:OnResized(frame.NameLabel, function(newSize, isPortrait)
|
||||
if wasIsPortrait ~= nil and wasIsPortrait == isPortrait then
|
||||
return
|
||||
end
|
||||
wasIsPortrait = isPortrait
|
||||
if isPortrait then
|
||||
if reportFlagAddedConnection == nil then
|
||||
reportFlagAddedConnection = frame.RightSideButtons.ChildAdded:connect(function(child)
|
||||
if child.Name == "ReportPlayer" then
|
||||
reportFlagChangedConnection = child.Changed:connect(function(prop) reportFlagChanged(child, prop) end)
|
||||
reportFlagChanged(child, "AbsolutePosition")
|
||||
end
|
||||
end)
|
||||
end
|
||||
local reportFlag = frame.RightSideButtons:FindFirstChild("ReportPlayer")
|
||||
if reportFlag then
|
||||
reportFlagChangedConnection = reportFlag.Changed:connect(function(prop) reportFlagChanged(reportFlag, prop) end)
|
||||
reportFlagChanged(reportFlag, "AbsolutePosition")
|
||||
end
|
||||
else
|
||||
frame.NameLabel.Text = player.Name
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function canShareCurrentGame()
|
||||
return this.HubRef.ShareGamePage ~= nil and localPlayer.UserId > 0
|
||||
end
|
||||
|
||||
local shareGameButton
|
||||
local sortedPlayers
|
||||
local existingPlayerLabels = {}
|
||||
local livePlayers = {}
|
||||
|
||||
this.Displayed.Event:connect(function(switchedFromGamepadInput)
|
||||
sortedPlayers = PlayersService:GetPlayers()
|
||||
table.sort(sortedPlayers, function(item1,item2)
|
||||
return item1.Name:lower() < item2.Name:lower()
|
||||
end)
|
||||
|
||||
local extraOffset = 20
|
||||
if utility:IsSmallTouchScreen() or utility:IsPortrait() then
|
||||
extraOffset = 85
|
||||
end
|
||||
|
||||
if IsShareGamePageEnabledByPlatform then
|
||||
-- Create "invite friends" button if it doesn't exist yet
|
||||
-- We shouldn't create this button if we're not in a live game
|
||||
if canShareCurrentGame() and not shareGameButton
|
||||
and (not RunService:IsStudio() or FFlagSettingsHubInviteToGameInStudio) then
|
||||
local eventStream = EventStream.new()
|
||||
shareGameButton = createShareGameButton()
|
||||
shareGameButton.Activated:connect(function()
|
||||
local eventContext = "inGame"
|
||||
local eventName = "inputShareGameEntryPoint"
|
||||
local additionalArgs = {
|
||||
buttonName = "settingsHub",
|
||||
}
|
||||
eventStream:setRBXEventStream(eventContext, eventName, additionalArgs)
|
||||
|
||||
this.HubRef:AddToMenuStack(this.HubRef.Pages.CurrentPage)
|
||||
this.HubRef:SwitchToPage(this.HubRef.ShareGamePage, nil, 1, true)
|
||||
end)
|
||||
|
||||
-- Ensure the button is always at the top of the list
|
||||
shareGameButton.LayoutOrder = 1
|
||||
shareGameButton.Parent = this.Page
|
||||
end
|
||||
end
|
||||
|
||||
friendSelectionFound = nil
|
||||
reportSelectionFound = nil
|
||||
|
||||
-- iterate through players to reuse or create labels for players
|
||||
for index=1, #sortedPlayers do
|
||||
local player = sortedPlayers[index]
|
||||
local frame
|
||||
frame = existingPlayerLabels[player.Name]
|
||||
|
||||
if player then
|
||||
livePlayers[player.Name] = true
|
||||
-- create label (frame) for this player index if one does not exist
|
||||
if not frame or not frame.Parent then
|
||||
frame = createPlayerRow((index - 1)*PLAYER_ROW_SPACING + extraOffset)
|
||||
frame.Parent = this.Page
|
||||
existingPlayerLabels[player.Name] = frame
|
||||
end
|
||||
frame.Name = "PlayerLabel" ..player.Name
|
||||
|
||||
-- Immediately assign the image to an image that isn't guaranteed to be generated
|
||||
frame.Icon.Image = SocialUtil.GetFallbackPlayerImageUrl(math.max(1, player.UserId), Enum.ThumbnailSize.Size100x100, Enum.ThumbnailType.AvatarThumbnail)
|
||||
-- Spawn a function to get the generated image
|
||||
spawn(function()
|
||||
local imageUrl = SocialUtil.GetPlayerImage(math.max(1, player.UserId), Enum.ThumbnailSize.Size100x100, Enum.ThumbnailType.AvatarThumbnail)
|
||||
if frame and frame.Parent and frame.Parent == this.Page then
|
||||
frame.Icon.Image = imageUrl
|
||||
end
|
||||
end)
|
||||
|
||||
frame.NameLabel.Text = player.Name
|
||||
frame.ImageTransparency = FRAME_DEFAULT_TRANSPARENCY
|
||||
if IsShareGamePageEnabledByPlatform then
|
||||
-- extra index room for shareGameButton
|
||||
frame.LayoutOrder = index + 1
|
||||
else
|
||||
frame.LayoutOrder = index
|
||||
end
|
||||
|
||||
managePlayerNameCutoff(frame, player)
|
||||
|
||||
friendStatusCreate(frame, player)
|
||||
if platform ~= Enum.Platform.XBoxOne and platform ~= Enum.Platform.PS4 then
|
||||
reportAbuseButtonCreate(frame, player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for playerName, frame in pairs(existingPlayerLabels) do
|
||||
if not livePlayers[playerName] then
|
||||
frame:Destroy()
|
||||
existingPlayerLabels[playerName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
utility:OnResized("MenuPlayerListExtraPageSize", function(newSize, isPortrait)
|
||||
local extraOffset = 20
|
||||
if utility:IsSmallTouchScreen() or utility:IsPortrait() then
|
||||
extraOffset = 85
|
||||
end
|
||||
|
||||
if IsShareGamePageEnabledByPlatform then
|
||||
local inviteToGameRow = 1
|
||||
local playerListRowsCount = #sortedPlayers + inviteToGameRow
|
||||
|
||||
this.Page.Size = UDim2.new(1,0,0, extraOffset + PLAYER_ROW_SPACING * playerListRowsCount - 5)
|
||||
else
|
||||
this.Page.Size = UDim2.new(1,0,0, extraOffset + PLAYER_ROW_SPACING * #sortedPlayers - 5)
|
||||
end
|
||||
|
||||
end)
|
||||
end)
|
||||
|
||||
PlayersService.PlayerRemoving:Connect(function (player)
|
||||
livePlayers[player.Name] = nil
|
||||
|
||||
local playerLabel = existingPlayerLabels[player.Name]
|
||||
|
||||
if not playerLabel then
|
||||
return
|
||||
end
|
||||
|
||||
local buttons = playerLabel:FindFirstChild("RightSideButtons")
|
||||
if not buttons then
|
||||
return
|
||||
end
|
||||
|
||||
local friendStatus = buttons:FindFirstChild("FriendStatus")
|
||||
if friendStatus then
|
||||
if GuiService.SelectedCoreObject == friendStatus then
|
||||
friendSelectionFound = nil
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
friendStatus:Destroy()
|
||||
end
|
||||
|
||||
local reportPlayer = buttons:FindFirstChild("ReportPlayer")
|
||||
if reportPlayer then
|
||||
if GuiService.SelectedCoreObject == reportPlayer then
|
||||
reportSelectionFound = nil
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
reportPlayer:Destroy()
|
||||
end
|
||||
end)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
PageInstance = Initialize()
|
||||
|
||||
return PageInstance
|
||||
@@ -0,0 +1,174 @@
|
||||
--[[r
|
||||
Filename: Record.lua
|
||||
Written by: jeditkacheff
|
||||
Version 1.0
|
||||
Description: Takes care of the Record Tab in Settings Menu
|
||||
--]]
|
||||
-------------- SERVICES --------------
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local TextService = game:GetService("TextService")
|
||||
local VRService = game:GetService("VRService")
|
||||
local Settings = UserSettings()
|
||||
local GameSettings = Settings.GameSettings
|
||||
|
||||
----------- UTILITIES --------------
|
||||
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
|
||||
|
||||
------------ Variables -------------------
|
||||
local PageInstance = nil
|
||||
|
||||
local success, result = pcall(function() return settings():GetFFlag('UseNotificationsLocalization') end)
|
||||
local FFlagUseNotificationsLocalization = success and result
|
||||
local FFlagKillGuiButtonSetVerb = settings():GetFFlag("KillGuiButtonSetVerb")
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
|
||||
local function Initialize()
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
local isRecordingVideo = false
|
||||
|
||||
local recordingEvent = Instance.new("BindableEvent")
|
||||
recordingEvent.Name = "RecordingEvent"
|
||||
this.RecordingChanged = recordingEvent.Event
|
||||
function this:IsRecording()
|
||||
return isRecordingVideo
|
||||
end
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader.Name = "RecordTab"
|
||||
|
||||
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/RecordTab.png"
|
||||
this.TabHeader.Icon.AspectRatioConstraint.AspectRatio = 41 / 40
|
||||
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.Text = "Record"
|
||||
else
|
||||
this.TabHeader.Icon.Title.Text = "Record"
|
||||
end
|
||||
|
||||
local function onVREnabled()
|
||||
this.TabHeader.Visible = not VRService.VREnabled
|
||||
end
|
||||
onVREnabled()
|
||||
VRService:GetPropertyChangedSignal("VREnabled"):connect(onVREnabled)
|
||||
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "Record"
|
||||
|
||||
local function makeTextLabel(name, text, isTitle, parent, layoutOrder)
|
||||
local leftPadding, rightPadding, bottomPadding, textSize, font = 10, 0, 10, 24, Enum.Font.SourceSans
|
||||
|
||||
if isTitle then
|
||||
leftPadding, rightPadding, bottomPadding, textSize, font = 10, 0, 0, 36, Enum.Font.SourceSansBold
|
||||
end
|
||||
|
||||
local container = utility:Create'Frame'
|
||||
{
|
||||
Name = name .. "Container",
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = 2,
|
||||
LayoutOrder = layoutOrder,
|
||||
Parent = parent
|
||||
};
|
||||
local textLabel = utility:Create'TextLabel'
|
||||
{
|
||||
Name = name,
|
||||
BackgroundTransparency = 1,
|
||||
Text = text,
|
||||
TextWrapped = true,
|
||||
Font = font,
|
||||
TextSize = textSize,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
Position = UDim2.new(0, leftPadding, 0, 0),
|
||||
Size = UDim2.new(1, -(leftPadding + rightPadding), 1, 0),
|
||||
ZIndex = 2,
|
||||
Parent = container
|
||||
};
|
||||
|
||||
local function onResized(prop)
|
||||
if prop == "AbsoluteSize" then
|
||||
local textSize = TextService:GetTextSize(text, textLabel.TextSize, textLabel.Font, Vector2.new(parent.AbsoluteSize.X - leftPadding - rightPadding, 1e4))
|
||||
container.Size = UDim2.new(1, 0, 0, textSize.Y + bottomPadding)
|
||||
end
|
||||
end
|
||||
onResized("AbsoluteSize")
|
||||
parent.Changed:connect(onResized)
|
||||
|
||||
return textLabel, container
|
||||
end
|
||||
|
||||
-- need to override this function from SettingsPageFactory
|
||||
-- DropDown menus require hub to to be set when they are initialized
|
||||
function this:SetHub(newHubRef)
|
||||
this.HubRef = newHubRef
|
||||
|
||||
---------------------------------- SCREENSHOT -------------------------------------
|
||||
local closeSettingsFunc = function()
|
||||
this.HubRef:SetVisibility(false, true)
|
||||
end
|
||||
|
||||
local screenshotTitle = makeTextLabel("ScreenshotTitle", "Screenshot", true, this.Page, 1)
|
||||
local screenshotBody = makeTextLabel("ScreenshotBody", "By clicking the 'Take Screenshot' button, the menu will close and take a screenshot and save it to your computer.", false, this.Page, 2)
|
||||
|
||||
this.ScreenshotButtonRow, this.ScreenshotButton = utility:AddButtonRow(this, "ScreenshotButton", "Take Screenshot", UDim2.new(0, 300, 0, 44), closeSettingsFunc)
|
||||
this.ScreenshotButtonRow.LayoutOrder = 3
|
||||
|
||||
---------------------------------- VIDEO -------------------------------------
|
||||
local videoTitle = makeTextLabel("VideoTitle", "Video", true, this.Page, 4)
|
||||
local videoBody = makeTextLabel("VideoBody", "By clicking the 'Record Video' button, the menu will close and start recording your screen.", false, this.Page, 5)
|
||||
|
||||
local recordButtonRow, recordButton = utility:AddButtonRow(this, "RecordButton", "Record Video", UDim2.new(0, 300, 0, 44), closeSettingsFunc)
|
||||
recordButtonRow.LayoutOrder = 6
|
||||
recordButton.MouseButton1Click:connect(function()
|
||||
recordingEvent:Fire(not isRecordingVideo)
|
||||
end)
|
||||
|
||||
local gameOptions = settings():FindFirstChild("Game Options")
|
||||
if gameOptions then
|
||||
gameOptions.VideoRecordingChangeRequest:connect(function(recording)
|
||||
isRecordingVideo = recording
|
||||
if recording then
|
||||
recordButton.RecordButtonTextLabel.Text = "Stop Recording"
|
||||
else
|
||||
recordButton.RecordButtonTextLabel.Text = "Record Video"
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if FFlagKillGuiButtonSetVerb then
|
||||
recordButton.Activated:Connect(function()
|
||||
CoreGui:ToggleRecording()
|
||||
end)
|
||||
this.ScreenshotButton.Activated:Connect(function()
|
||||
CoreGui:TakeScreenshot()
|
||||
end)
|
||||
else
|
||||
recordButton:SetVerb("RecordToggle")
|
||||
this.ScreenshotButton:SetVerb("Screenshot")
|
||||
end
|
||||
|
||||
this.Page.Size = UDim2.new(1,0,0,400)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
PageInstance = Initialize()
|
||||
|
||||
PageInstance.Displayed.Event:connect(function(switchedFromGamepadInput)
|
||||
if switchedFromGamepadInput then
|
||||
GuiService.SelectedCoreObject = PageInstance.ScreenshotButton
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
return PageInstance
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
--[[
|
||||
Filename: ReportAbuseMenu.lua
|
||||
Written by: jeditkacheff
|
||||
Version 1.0
|
||||
Description: Takes care of the report abuse page in Settings Menu
|
||||
--]]
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local PlayersService = game:GetService("Players")
|
||||
local MarketplaceService = game:GetService("MarketplaceService")
|
||||
local LocalizationService = game:GetService("LocalizationService")
|
||||
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local ABUSE_TYPES_PLAYER = {
|
||||
"Swearing",
|
||||
"Inappropriate Username",
|
||||
"Bullying",
|
||||
"Scamming",
|
||||
"Dating",
|
||||
"Cheating/Exploiting",
|
||||
"Personal Question",
|
||||
"Offsite Links",
|
||||
}
|
||||
|
||||
local FFlagReportAbuseMenuDescriptionFix = settings():GetFFlag('ReportAbuseMenuDescriptionFix')
|
||||
|
||||
local ABUSE_TYPES_GAME = {
|
||||
"Inappropriate Content"
|
||||
}
|
||||
|
||||
local DEFAULT_ABUSE_DESC_TEXT = " Short Description (Optional)"
|
||||
if utility:IsSmallTouchScreen() then
|
||||
DEFAULT_ABUSE_DESC_TEXT = " (Optional)"
|
||||
end
|
||||
|
||||
pcall(function()
|
||||
local LocalizationService = game:GetService("LocalizationService")
|
||||
local CorescriptLocalization = LocalizationService:GetCorescriptLocalizations()[1]
|
||||
|
||||
if FFlagReportAbuseMenuDescriptionFix then
|
||||
if utility:IsSmallTouchScreen() then
|
||||
DEFAULT_ABUSE_DESC_TEXT = RobloxTranslator:FormatByKey("KEY_DESCRIPTION_OPTIONAL")
|
||||
else
|
||||
DEFAULT_ABUSE_DESC_TEXT = RobloxTranslator:FormatByKey("KEY_DESCRIPTION_SHORT_DECRIPTION_OPTIONAL")
|
||||
end
|
||||
else
|
||||
DEFAULT_ABUSE_DESC_TEXT = CorescriptLocalization:GetString(
|
||||
LocalizationService.RobloxLocaleId,
|
||||
"KEY_DESCRIPTION_OPTIONAL")
|
||||
end
|
||||
end)
|
||||
|
||||
local PageInstance = nil
|
||||
|
||||
local success, result = pcall(function() return settings():GetFFlag('UseNotificationsLocalization') end)
|
||||
local FFlagUseNotificationsLocalization = success and result
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
local function Initialize()
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
|
||||
local playerNames = {}
|
||||
local nameToRbxPlayer = {}
|
||||
local nextPlayerToReport = nil
|
||||
|
||||
function this:GetPlayerFromIndex(index)
|
||||
local playerName = playerNames[index]
|
||||
if playerName then
|
||||
return nameToRbxPlayer[playerName]
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function this:SetNextPlayerToReport(player)
|
||||
nextPlayerToReport = player
|
||||
end
|
||||
|
||||
function this:UpdatePlayerDropDown()
|
||||
playerNames = {}
|
||||
nameToRbxPlayer = {}
|
||||
|
||||
local players = PlayersService:GetPlayers()
|
||||
local index = 1
|
||||
for i = 1, #players do
|
||||
local player = players[i]
|
||||
if player ~= PlayersService.LocalPlayer and player.UserId > 0 then
|
||||
playerNames[index] = player.Name
|
||||
nameToRbxPlayer[player.Name] = player
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(playerNames, function(a, b)
|
||||
return a:lower() < b:lower()
|
||||
end)
|
||||
|
||||
this.WhichPlayerMode:UpdateDropDownList(playerNames)
|
||||
|
||||
--Reset GameOrPlayerMode to Game if no other players
|
||||
if index == 1 then
|
||||
this.GameOrPlayerMode:SetSelectionIndex(1)
|
||||
end
|
||||
|
||||
this.GameOrPlayerMode:SetInteractable(index > 1)
|
||||
|
||||
if this.GameOrPlayerMode.CurrentIndex == 1 then
|
||||
this.WhichPlayerMode:SetInteractable(false)
|
||||
this.TypeOfAbuseMode:UpdateDropDownList(ABUSE_TYPES_GAME)
|
||||
this.TypeOfAbuseMode:SetInteractable(#ABUSE_TYPES_GAME > 1)
|
||||
else
|
||||
this.WhichPlayerLabel.ZIndex = 2
|
||||
this.WhichPlayerMode:SetInteractable(index > 1)
|
||||
this.TypeOfAbuseMode:UpdateDropDownList(ABUSE_TYPES_PLAYER)
|
||||
this.TypeOfAbuseMode:SetInteractable(#ABUSE_TYPES_PLAYER > 1)
|
||||
end
|
||||
|
||||
if nextPlayerToReport then
|
||||
local playerSelected = this.WhichPlayerMode:SetSelectionByValue(nextPlayerToReport.Name)
|
||||
nextPlayerToReport = nil
|
||||
|
||||
if this.GameOrPlayerMode.CurrentIndex == 2 then
|
||||
if playerSelected then --if the reported player is still in game
|
||||
--Auto select type of abuse when report a player
|
||||
GuiService.SelectedCoreObject = this.TypeOfAbuseMode.DropDownFrame
|
||||
else
|
||||
GuiService.SelectedCoreObject = this.WhichPlayerMode.DropDownFrame
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader.Name = "ReportAbuseTab"
|
||||
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/ReportAbuseTab.png"
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.Text = "Report"
|
||||
else
|
||||
this.TabHeader.Icon.Title.Text = "Report"
|
||||
end
|
||||
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "ReportAbusePage"
|
||||
|
||||
-- need to override this function from SettingsPageFactory
|
||||
-- DropDown menus require hub to to be set when they are initialized
|
||||
function this:SetHub(newHubRef)
|
||||
this.HubRef = newHubRef
|
||||
|
||||
if utility:IsSmallTouchScreen() then
|
||||
this.GameOrPlayerFrame,
|
||||
this.GameOrPlayerLabel,
|
||||
this.GameOrPlayerMode = utility:AddNewRow(this, "Game or Player?", "Selector", {"Game", "Player"}, 2)
|
||||
else
|
||||
this.GameOrPlayerFrame,
|
||||
this.GameOrPlayerLabel,
|
||||
this.GameOrPlayerMode = utility:AddNewRow(this, "Game or Player?", "Selector", {"Game", "Player"}, 2, 3)
|
||||
end
|
||||
this.GameOrPlayerMode.Selection.LayoutOrder = 1
|
||||
|
||||
this.WhichPlayerFrame,
|
||||
this.WhichPlayerLabel,
|
||||
this.WhichPlayerMode = utility:AddNewRow(this, "Which Player?", "DropDown", {"update me"})
|
||||
this.WhichPlayerMode:SetInteractable(false)
|
||||
this.WhichPlayerLabel.ZIndex = 1
|
||||
this.WhichPlayerFrame.LayoutOrder = 2
|
||||
|
||||
this.TypeOfAbuseFrame,
|
||||
this.TypeOfAbuseLabel,
|
||||
this.TypeOfAbuseMode = utility:AddNewRow(this, "Type Of Abuse", "DropDown", ABUSE_TYPES_GAME, 1)
|
||||
this.TypeOfAbuseFrame.LayoutOrder = 3
|
||||
|
||||
if utility:IsSmallTouchScreen() then
|
||||
this.AbuseDescriptionFrame,
|
||||
this.AbuseDescriptionLabel,
|
||||
this.AbuseDescription = utility:AddNewRow(this, DEFAULT_ABUSE_DESC_TEXT, "TextBox", nil, nil, 5)
|
||||
|
||||
this.AbuseDescriptionLabel.Text = "Abuse Description"
|
||||
else
|
||||
this.AbuseDescriptionFrame,
|
||||
this.AbuseDescriptionLabel,
|
||||
this.AbuseDescription = utility:AddNewRow(this, DEFAULT_ABUSE_DESC_TEXT, "TextBox", nil, nil, 5)
|
||||
|
||||
this.AbuseDescriptionFrame.Size = UDim2.new(1, -10, 0, 100)
|
||||
this.AbuseDescription.Selection.Size = UDim2.new(1, 0, 1, 0)
|
||||
end
|
||||
|
||||
this.AbuseDescriptionFrame.LayoutOrder = 4
|
||||
|
||||
this.AbuseDescription.Selection.FocusLost:connect(function()
|
||||
if this.AbuseDescription.Selection.Text == "" then
|
||||
this.AbuseDescription.Selection.Text = DEFAULT_ABUSE_DESC_TEXT
|
||||
end
|
||||
end)
|
||||
|
||||
local SelectionOverrideObject = utility:Create'ImageLabel'
|
||||
{
|
||||
Image = "",
|
||||
BackgroundTransparency = 1
|
||||
};
|
||||
|
||||
local submitButton, submitText = nil, nil
|
||||
|
||||
local function makeSubmitButtonActive()
|
||||
submitButton.ZIndex = 2
|
||||
submitButton.Selectable = true
|
||||
submitText.ZIndex = 2
|
||||
end
|
||||
|
||||
local function makeSubmitButtonInactive()
|
||||
submitButton.ZIndex = 1
|
||||
submitButton.Selectable = false
|
||||
submitText.ZIndex = 1
|
||||
end
|
||||
|
||||
local function updateAbuseDropDown()
|
||||
this.WhichPlayerMode:ResetSelectionIndex()
|
||||
this.TypeOfAbuseMode:ResetSelectionIndex()
|
||||
|
||||
if this.GameOrPlayerMode.CurrentIndex == 1 then
|
||||
this.TypeOfAbuseMode:UpdateDropDownList(ABUSE_TYPES_GAME)
|
||||
|
||||
this.TypeOfAbuseMode:SetInteractable(#ABUSE_TYPES_GAME > 1)
|
||||
this.TypeOfAbuseLabel.ZIndex = (#ABUSE_TYPES_GAME > 1 and 2 or 1)
|
||||
|
||||
this.WhichPlayerMode:SetInteractable(false)
|
||||
this.WhichPlayerLabel.ZIndex = 1
|
||||
makeSubmitButtonActive()
|
||||
else
|
||||
this.TypeOfAbuseMode:UpdateDropDownList(ABUSE_TYPES_PLAYER)
|
||||
this.TypeOfAbuseMode:SetInteractable(#ABUSE_TYPES_PLAYER > 1)
|
||||
this.TypeOfAbuseLabel.ZIndex = (#ABUSE_TYPES_PLAYER > 1 and 2 or 1)
|
||||
|
||||
if #playerNames > 0 then
|
||||
this.WhichPlayerMode:SetInteractable(true)
|
||||
this.WhichPlayerLabel.ZIndex = 2
|
||||
else
|
||||
this.WhichPlayerMode:SetInteractable(false)
|
||||
this.WhichPlayerLabel.ZIndex = 1
|
||||
end
|
||||
makeSubmitButtonInactive()
|
||||
end
|
||||
end
|
||||
|
||||
local function cleanupReportAbuseMenu()
|
||||
updateAbuseDropDown()
|
||||
this.AbuseDescription.Selection.Text = DEFAULT_ABUSE_DESC_TEXT
|
||||
this.HubRef:SetVisibility(false, true)
|
||||
end
|
||||
|
||||
local function onReportSubmitted()
|
||||
local abuseReason = nil
|
||||
local reportSucceeded = false
|
||||
if this.GameOrPlayerMode.CurrentIndex == 2 then
|
||||
abuseReason = ABUSE_TYPES_PLAYER[this.TypeOfAbuseMode.CurrentIndex]
|
||||
|
||||
local currentAbusingPlayer = this:GetPlayerFromIndex(this.WhichPlayerMode.CurrentIndex)
|
||||
if currentAbusingPlayer and abuseReason then
|
||||
reportSucceeded = true
|
||||
spawn(function()
|
||||
PlayersService:ReportAbuse(currentAbusingPlayer, abuseReason, this.AbuseDescription.Selection.Text)
|
||||
end)
|
||||
end
|
||||
else
|
||||
abuseReason = ABUSE_TYPES_GAME[this.TypeOfAbuseMode.CurrentIndex]
|
||||
if abuseReason then
|
||||
reportSucceeded = true
|
||||
spawn(function()
|
||||
local placeId,placeName,placeDescription = tostring(game.PlaceId), "N/A", "N/A"
|
||||
local abuseDescription = this.AbuseDescription.Selection.Text
|
||||
pcall(function()
|
||||
local productInfo = MarketplaceService:GetProductInfo(game.PlaceId, Enum.InfoType.Asset)
|
||||
placeName = productInfo.Name
|
||||
placeDescription = productInfo.Description
|
||||
end)
|
||||
local formattedText = string.format("User Report: \n %s \n".."Place Title: \n %s \n".."PlaceId: \n %s \n".."Place Description: \n %s \n",abuseDescription, placeName, placeId, placeDescription)
|
||||
|
||||
PlayersService:ReportAbuse(nil, abuseReason, formattedText)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
if reportSucceeded then
|
||||
local alertText = "Thanks for your report! Our moderators will review the chat logs and evaluate what happened."
|
||||
|
||||
if abuseReason == 'Cheating/Exploiting' then
|
||||
alertText = "Thanks for your report! We've recorded your report for evaluation."
|
||||
elseif abuseReason == 'Inappropriate Username' then
|
||||
alertText = "Thanks for your report! Our moderators will evaluate the username."
|
||||
elseif abuseReason == "Bad Model or Script" or abuseReason == "Inappropriate Content" or abuseReason == "Offsite Link" or abuseReason == "Offsite Links" then
|
||||
alertText = "Thanks for your report! Our moderators will review the place and make a determination."
|
||||
end
|
||||
|
||||
utility:ShowAlert(alertText, "Ok", this.HubRef, cleanupReportAbuseMenu)
|
||||
|
||||
this.LastSelectedObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
submitButton, submitText = utility:MakeStyledButton("SubmitButton", "Submit", UDim2.new(0,198,0,50), onReportSubmitted, this)
|
||||
|
||||
submitButton.AnchorPoint = Vector2.new(0.5,0)
|
||||
submitButton.Position = UDim2.new(0.5,0,1,5)
|
||||
|
||||
if this.GameOrPlayerMode.CurrentIndex == 1 then
|
||||
makeSubmitButtonActive()
|
||||
else
|
||||
makeSubmitButtonInactive()
|
||||
end
|
||||
submitButton.Parent = this.AbuseDescription.Selection
|
||||
|
||||
local function playerSelectionChanged(newIndex)
|
||||
if newIndex ~= nil and this.TypeOfAbuseMode:GetSelectedIndex() ~= nil then
|
||||
makeSubmitButtonActive()
|
||||
else
|
||||
makeSubmitButtonInactive()
|
||||
end
|
||||
end
|
||||
this.WhichPlayerMode.IndexChanged:connect(playerSelectionChanged)
|
||||
|
||||
local function typeOfAbuseChanged(newIndex)
|
||||
if newIndex ~= nil then
|
||||
if this.GameOrPlayerMode.CurrentIndex == 1 then -- 1 is Report Game
|
||||
makeSubmitButtonActive()
|
||||
else -- 2 is Report Player
|
||||
if this.WhichPlayerMode:GetSelectedIndex() then
|
||||
makeSubmitButtonActive()
|
||||
else
|
||||
makeSubmitButtonInactive()
|
||||
end
|
||||
end
|
||||
else
|
||||
makeSubmitButtonInactive()
|
||||
end
|
||||
end
|
||||
this.TypeOfAbuseMode.IndexChanged:connect(typeOfAbuseChanged)
|
||||
|
||||
this.GameOrPlayerMode.IndexChanged:connect(updateAbuseDropDown)
|
||||
|
||||
this:AddRow(nil, nil, this.AbuseDescription)
|
||||
|
||||
this.Page.Size = UDim2.new(1,0,0,submitButton.AbsolutePosition.Y + submitButton.AbsoluteSize.Y)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
do
|
||||
PageInstance = Initialize()
|
||||
|
||||
PageInstance.Displayed.Event:connect(function()
|
||||
PageInstance:UpdatePlayerDropDown()
|
||||
end)
|
||||
|
||||
function PageInstance:ReportPlayer(player)
|
||||
if player then
|
||||
local setReportPlayerConnection = nil
|
||||
PageInstance:SetNextPlayerToReport(player)
|
||||
setReportPlayerConnection = PageInstance.Displayed.Event:connect(function()
|
||||
PageInstance.GameOrPlayerMode:SetSelectionIndex(2)
|
||||
|
||||
if setReportPlayerConnection then
|
||||
setReportPlayerConnection:disconnect()
|
||||
setReportPlayerConnection = nil
|
||||
end
|
||||
end)
|
||||
|
||||
if not PageInstance.HubRef:GetVisibility() then
|
||||
PageInstance.HubRef:SetVisibility(true, false, PageInstance)
|
||||
else
|
||||
PageInstance.HubRef:SwitchToPage(PageInstance, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return PageInstance
|
||||
@@ -0,0 +1,165 @@
|
||||
--[[
|
||||
Filename: ResetCharacter.lua
|
||||
Written by: jeditkacheff
|
||||
Version 1.0
|
||||
Description: Takes care of the reseting the character in Settings Menu
|
||||
--]]
|
||||
|
||||
-------------- CONSTANTS -------------
|
||||
local RESET_CHARACTER_GAME_ACTION = "ResetCharacterAction"
|
||||
|
||||
-------------- SERVICES --------------
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local PlayersService = game:GetService("Players")
|
||||
|
||||
----------- UTILITIES --------------
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
|
||||
------------ Variables -------------------
|
||||
local PageInstance = nil
|
||||
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
|
||||
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
|
||||
local function Initialize()
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
|
||||
this.DontResetCharFunc = function(isUsingGamepad)
|
||||
if this.HubRef then
|
||||
this.HubRef:PopMenu(isUsingGamepad, true)
|
||||
end
|
||||
end
|
||||
this.DontResetCharFromHotkey = function(name, state, input)
|
||||
if state == Enum.UserInputState.Begin then
|
||||
local isUsingGamepad = input.UserInputType == Enum.UserInputType.Gamepad1 or input.UserInputType == Enum.UserInputType.Gamepad2
|
||||
or input.UserInputType == Enum.UserInputType.Gamepad3 or input.UserInputType == Enum.UserInputType.Gamepad4
|
||||
|
||||
this.DontResetCharFunc(isUsingGamepad)
|
||||
end
|
||||
end
|
||||
this.DontResetCharFromButton = function(isUsingGamepad)
|
||||
this.DontResetCharFunc(isUsingGamepad)
|
||||
end
|
||||
|
||||
------ TAB CUSTOMIZATION -------
|
||||
this.TabHeader = nil -- no tab for this page
|
||||
|
||||
------ PAGE CUSTOMIZATION -------
|
||||
this.Page.Name = "ResetCharacter"
|
||||
this.ShouldShowBottomBar = false
|
||||
this.ShouldShowHubBar = false
|
||||
|
||||
local resetCharacterText = utility:Create'TextLabel'
|
||||
{
|
||||
Name = "ResetCharacterText",
|
||||
Text = "Are you sure you want to reset your character?",
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size36,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1,0,0,200),
|
||||
TextWrapped = true,
|
||||
ZIndex = 2,
|
||||
Parent = this.Page,
|
||||
Position = isTenFootInterface and UDim2.new(0,0,0,100) or UDim2.new(0,0,0,0)
|
||||
};
|
||||
|
||||
local resetButtonContainer = utility:Create"Frame"
|
||||
{
|
||||
Name = "ResetButtonContainer",
|
||||
Parent = resetCharacterText,
|
||||
Size = UDim2.new(1,0,0,400),
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0,0,1,0)
|
||||
};
|
||||
|
||||
local resetButtonLayout = utility:Create'UIGridLayout'
|
||||
{
|
||||
Name = "ResetButtonsLayout",
|
||||
CellSize = isTenFootInterface and UDim2.new(0, 300, 0, 80) or UDim2.new(0, 200, 0, 50),
|
||||
CellPadding = UDim2.new(0,20,0,20),
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
Parent = resetButtonContainer
|
||||
};
|
||||
|
||||
if utility:IsSmallTouchScreen() then
|
||||
resetCharacterText.FontSize = Enum.FontSize.Size24
|
||||
resetCharacterText.Size = UDim2.new(1,0,0,100)
|
||||
elseif isTenFootInterface then
|
||||
resetCharacterText.FontSize = Enum.FontSize.Size48
|
||||
end
|
||||
|
||||
------ Init -------
|
||||
local resetCharFunc = function()
|
||||
local player = PlayersService.LocalPlayer
|
||||
if player then
|
||||
local character = player.Character
|
||||
if character then
|
||||
local humanoid = character:FindFirstChild('Humanoid')
|
||||
if humanoid then
|
||||
humanoid.Health = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
this.ResetBindable = true
|
||||
|
||||
local onResetFunction = function()
|
||||
if this.ResetBindable == true then
|
||||
resetCharFunc()
|
||||
elseif this.ResetBindable then
|
||||
this.ResetBindable:Fire()
|
||||
end
|
||||
if this.HubRef then
|
||||
this.HubRef:SetVisibility(false, true)
|
||||
end
|
||||
end
|
||||
|
||||
this.ResetCharacterButton = utility:MakeStyledButton("ResetCharacter", "Reset", nil, onResetFunction)
|
||||
this.ResetCharacterButton.NextSelectionRight = nil
|
||||
this.ResetCharacterButton.Parent = resetButtonContainer
|
||||
|
||||
|
||||
local dontResetCharacterButton = utility:MakeStyledButton("DontResetCharacter", "Don't Reset", nil, this.DontResetCharFromButton)
|
||||
dontResetCharacterButton.NextSelectionLeft = nil
|
||||
dontResetCharacterButton.Parent = resetButtonContainer
|
||||
|
||||
this.Page.Size = UDim2.new(1,0,0,dontResetCharacterButton.AbsolutePosition.Y + dontResetCharacterButton.AbsoluteSize.Y)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
----------- Public Facing API Additions --------------
|
||||
PageInstance = Initialize()
|
||||
local isOpen = false
|
||||
|
||||
PageInstance.Displayed.Event:connect(function()
|
||||
isOpen = true
|
||||
GuiService.SelectedCoreObject = PageInstance.ResetCharacterButton
|
||||
ContextActionService:BindCoreAction(RESET_CHARACTER_GAME_ACTION, PageInstance.DontResetCharFromHotkey, false, Enum.KeyCode.ButtonB)
|
||||
end)
|
||||
|
||||
PageInstance.Hidden.Event:connect(function()
|
||||
isOpen = false
|
||||
ContextActionService:UnbindCoreAction(RESET_CHARACTER_GAME_ACTION)
|
||||
end)
|
||||
|
||||
function PageInstance:SetResetCallback(bindableEvent)
|
||||
if bindableEvent == false and isOpen then
|
||||
-- We need to close this page if reseting was just disabled and the page is already open
|
||||
PageInstance.HubRef:PopMenu(nil, true)
|
||||
end
|
||||
PageInstance.ResetBindable = bindableEvent
|
||||
end
|
||||
|
||||
return PageInstance
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function(route)
|
||||
return {
|
||||
route = route,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function(route)
|
||||
return {
|
||||
route = route,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function(userId, inviteStatus)
|
||||
return {
|
||||
userId = tostring(userId),
|
||||
inviteStatus = inviteStatus,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function(deviceLayout)
|
||||
return {
|
||||
deviceLayout = deviceLayout,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function(isSmallTouchScreen)
|
||||
return {
|
||||
isSmallTouchScreen = isSmallTouchScreen,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function(isActive)
|
||||
return {
|
||||
isActive = isActive,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function(searchText)
|
||||
return {
|
||||
searchText = searchText,
|
||||
}
|
||||
end)
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action(script.Name, function()
|
||||
return {}
|
||||
end)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local PlaceInfos = require(AppTempCommon.LuaChat.Reducers.PlaceInfos)
|
||||
local Users = require(AppTempCommon.LuaApp.Reducers.Users)
|
||||
local Friends = require(AppTempCommon.LuaApp.Reducers.Friends)
|
||||
|
||||
local ConversationsSearch = require(ShareGame.Reducers.ConversationsSearch)
|
||||
local DeviceInfo = require(ShareGame.Reducers.DeviceInfo)
|
||||
local Invites = require(ShareGame.Reducers.Invites)
|
||||
local Page = require(ShareGame.Reducers.Page)
|
||||
local Toasts = require(ShareGame.Reducers.Toasts)
|
||||
|
||||
return function(state, action)
|
||||
state = state or {}
|
||||
|
||||
return {
|
||||
ConversationsSearch = ConversationsSearch(state.ConversationsSearch, action),
|
||||
DeviceInfo = DeviceInfo(state.DeviceInfo, action),
|
||||
Invites = Invites(state.Invites, action),
|
||||
Page = Page(state.Page, action),
|
||||
PlaceInfos = PlaceInfos(state.PlaceInfos, action),
|
||||
Toasts = Toasts(state.Toasts, action),
|
||||
Users = Users(state.Users, action),
|
||||
Friends = Friends(state.Friends, action),
|
||||
}
|
||||
end
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
local Modules = CoreGui.RobloxGui.Modules
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local EventStream = require(AppTempCommon.Temp.EventStream)
|
||||
|
||||
local LayoutProvider = require(ShareGame.Components.LayoutProvider)
|
||||
local ShareGamePageFrame = require(ShareGame.Components.ShareGamePageFrame)
|
||||
|
||||
local ShareGameApp = Roact.PureComponent:extend("App")
|
||||
|
||||
function ShareGameApp:render()
|
||||
local pageTarget = self.props.pageTarget
|
||||
|
||||
local pageFrame = nil
|
||||
if self.props.isPageOpen then
|
||||
pageFrame = Roact.createElement(ShareGamePageFrame, {
|
||||
zIndex = Constants.SHARE_GAME_Z_INDEX,
|
||||
})
|
||||
end
|
||||
|
||||
return Roact.createElement(LayoutProvider, nil, {
|
||||
Roact.createElement(Roact.Portal, {
|
||||
target = pageTarget,
|
||||
}, {
|
||||
ShareGamePageFrame = pageFrame
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
function ShareGameApp:didMount()
|
||||
self.eventStream = EventStream.new()
|
||||
end
|
||||
|
||||
function ShareGameApp:willUnmount()
|
||||
self.eventStream:releaseRBXEventStream()
|
||||
end
|
||||
|
||||
local connector = RoactRodux.UNSTABLE_connect2(function(state, props)
|
||||
return {
|
||||
isPageOpen = state.Page.IsOpen,
|
||||
}
|
||||
end)
|
||||
|
||||
return connector(ShareGameApp)
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
local RectangleButton = require(ShareGame.Components.RectangleButton)
|
||||
local IconButton = require(ShareGame.Components.IconButton)
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local ShareGameIcons = require(RobloxGui.Modules.Settings.Pages.ShareGame.Spritesheets.ShareGameIcons)
|
||||
|
||||
local BACK_IMAGE_SPRITE_PATH = ShareGameIcons:GetImagePath()
|
||||
local BACK_IMAGE_SPRITE_FRAME = ShareGameIcons:GetFrame("back")
|
||||
local BACK_BUTTON_TEXT_SIZE = 24
|
||||
|
||||
local BackButton = Roact.PureComponent:extend("BackButton")
|
||||
|
||||
function BackButton:render()
|
||||
local isArrow = self.props.isArrow
|
||||
local visible = self.props.visible
|
||||
local anchorPoint = self.props.anchorPoint
|
||||
local position = self.props.position
|
||||
local size = self.props.size
|
||||
local zIndex = self.props.zIndex
|
||||
local onClick = self.props.onClick
|
||||
|
||||
if isArrow then
|
||||
return Roact.createElement(IconButton, {
|
||||
visible = visible,
|
||||
anchorPoint = anchorPoint,
|
||||
position = position,
|
||||
size = size,
|
||||
zIndex = zIndex,
|
||||
onClick = onClick,
|
||||
iconHorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||
iconSpritePath = BACK_IMAGE_SPRITE_PATH,
|
||||
iconSpriteFrame = BACK_IMAGE_SPRITE_FRAME,
|
||||
})
|
||||
else
|
||||
return Roact.createElement(RectangleButton, self.props, {
|
||||
TextLabel = Roact.createElement("TextLabel", {
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
Text = RobloxTranslator:FormatByKey("Feature.SettingsHub.Action.InviteFriendsBack"),
|
||||
TextSize = BACK_BUTTON_TEXT_SIZE,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
Font = Enum.Font.SourceSansSemibold,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return BackButton
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local User = require(AppTempCommon.LuaApp.Models.User)
|
||||
|
||||
local LIST_PADDING = 2
|
||||
|
||||
local TITLE_FONT = Enum.Font.SourceSans
|
||||
local TITLE_COLOR = Constants.Color.WHITE
|
||||
local TITLE_TEXT_SIZE = 19
|
||||
|
||||
local PRESENCE_FONT = Enum.Font.SourceSans
|
||||
local PRESENCE_TEXT_SIZE = 16
|
||||
|
||||
local ConversationDetails = Roact.PureComponent:extend("ConversationDetails")
|
||||
|
||||
function ConversationDetails:render()
|
||||
local title = self.props.title
|
||||
local presence = self.props.presence
|
||||
local size = self.props.size
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local zIndex = self.props.zIndex
|
||||
|
||||
-- Show user presence if it was passed in
|
||||
local presenceTextComponent
|
||||
if presence ~= nil then
|
||||
presenceTextComponent = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, PRESENCE_TEXT_SIZE),
|
||||
Text = RobloxTranslator:FormatByKey(Constants.PresenceTextKey[presence]),
|
||||
Font = PRESENCE_FONT,
|
||||
TextColor3 = Constants.PresenceColors[presence],
|
||||
TextSize = PRESENCE_TEXT_SIZE,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
LayoutOrder = 1,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = size,
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, LIST_PADDING)
|
||||
}),
|
||||
Title = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, TITLE_TEXT_SIZE),
|
||||
Text = title or "",
|
||||
Font = TITLE_FONT,
|
||||
TextColor3 = TITLE_COLOR,
|
||||
TextSize = TITLE_TEXT_SIZE,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextWrapped = true,
|
||||
LayoutOrder = 0,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
Presence = presenceTextComponent,
|
||||
})
|
||||
end
|
||||
|
||||
return ConversationDetails
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local ConversationDetails = require(ShareGame.Components.ConversationDetails)
|
||||
local ConversationThumbnail = require(ShareGame.Components.ConversationThumbnail)
|
||||
local EventStream = require(CorePackages.AppTempCommon.Temp.EventStream)
|
||||
local InviteButton = require(ShareGame.Components.InviteButton)
|
||||
|
||||
local ENTRY_BG_IMAGE = "rbxasset://textures/ui/dialog_white.png"
|
||||
local ENTRY_BG_SLICE = Rect.new(10, 10, 10, 10)
|
||||
local ENTRY_BG_TRANSPARENCY = 0.85
|
||||
|
||||
local THUMBNAIL_SIZE = 32
|
||||
local INVITE_BUTTON_WIDTH = 69
|
||||
local CONTENTS_PADDING = 12
|
||||
|
||||
local ConversationEntry = Roact.PureComponent:extend("ConversationEntry")
|
||||
|
||||
function ConversationEntry:init()
|
||||
self.eventStream = EventStream.new()
|
||||
end
|
||||
|
||||
function ConversationEntry:render()
|
||||
local visible = self.props.visible
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local zIndex = self.props.zIndex
|
||||
local size = self.props.size
|
||||
local title = self.props.title
|
||||
local users = self.props.users
|
||||
local inviteUser = self.props.inviteUser
|
||||
local inviteStatus = self.props.inviteStatus
|
||||
|
||||
-- Presence gets passed in if there's only one user
|
||||
local presence = self.props.presence
|
||||
|
||||
return Roact.createElement("ImageLabel", {
|
||||
Visible = visible,
|
||||
BackgroundTransparency = 1,
|
||||
Image = ENTRY_BG_IMAGE,
|
||||
ImageTransparency = ENTRY_BG_TRANSPARENCY,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = ENTRY_BG_SLICE,
|
||||
Size = size,
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
UIPadding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, CONTENTS_PADDING),
|
||||
PaddingRight = UDim.new(0, CONTENTS_PADDING),
|
||||
PaddingTop = UDim.new(0, CONTENTS_PADDING),
|
||||
PaddingBottom = UDim.new(0, CONTENTS_PADDING),
|
||||
}),
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, 12),
|
||||
}),
|
||||
|
||||
Thumbnail = Roact.createElement(ConversationThumbnail, {
|
||||
users = users,
|
||||
size = UDim2.new(0, THUMBNAIL_SIZE, 0, THUMBNAIL_SIZE),
|
||||
layoutOrder = 0,
|
||||
zIndex = zIndex,
|
||||
}),
|
||||
Details = Roact.createElement(ConversationDetails, {
|
||||
title = title,
|
||||
presence = presence,
|
||||
size = UDim2.new(
|
||||
-- Make details fullwidth and subtract the width of its siblings
|
||||
1, -(THUMBNAIL_SIZE + INVITE_BUTTON_WIDTH + CONTENTS_PADDING * 2),
|
||||
1, 0
|
||||
),
|
||||
layoutOrder = 1,
|
||||
zIndex = zIndex,
|
||||
}),
|
||||
InviteButton = Roact.createElement(InviteButton, {
|
||||
size = UDim2.new(0, INVITE_BUTTON_WIDTH, 1, 0),
|
||||
layoutOrder = 2,
|
||||
zIndex = zIndex,
|
||||
onInvite = function()
|
||||
-- Check if this is a one-on-one convo
|
||||
if #users == 1 then
|
||||
inviteUser(users[1].id):andThen(function(results)
|
||||
if not results then
|
||||
return
|
||||
end
|
||||
|
||||
-- Pluck the userIds out of the user list
|
||||
local participants = {}
|
||||
for _, user in pairs(users) do
|
||||
table.insert(participants, user.id)
|
||||
end
|
||||
|
||||
local localPlayer = Players.LocalPlayer
|
||||
local senderId = tostring(localPlayer.UserId)
|
||||
|
||||
local eventContext = "inGame"
|
||||
local eventName = "clickShareGameInviteSent"
|
||||
local participantsString = table.concat(participants, ",")
|
||||
local additionalArgs = {
|
||||
btn = "settingsHub",
|
||||
placeId = tostring(results.placeId),
|
||||
senderId = senderId,
|
||||
conversationId = tostring(results.conversationId),
|
||||
participants = participantsString,
|
||||
wasModerated = results.wasModerated,
|
||||
}
|
||||
|
||||
self.eventStream:setRBXEventStream(eventContext, eventName, additionalArgs)
|
||||
end)
|
||||
end
|
||||
end,
|
||||
inviteStatus = inviteStatus,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return ConversationEntry
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local HttpRbxApiService = game:GetService("HttpRbxApiService")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
local ConversationEntry = require(ShareGame.Components.ConversationEntry)
|
||||
local NoFriendsPage = require(ShareGame.Components.NoFriendsPage)
|
||||
local LoadingFriendsPage = require(ShareGame.Components.LoadingFriendsPage)
|
||||
local FriendsErrorPage = require(ShareGame.Components.FriendsErrorPage)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local httpRequest = require(AppTempCommon.Temp.httpRequest)
|
||||
|
||||
local User = require(AppTempCommon.LuaApp.Models.User)
|
||||
local ReceivedUserInviteStatus = require(ShareGame.Actions.ReceivedUserInviteStatus)
|
||||
local memoize = require(AppTempCommon.Common.memoize)
|
||||
local Promise = require(AppTempCommon.LuaApp.Promise)
|
||||
|
||||
local ApiSendGameInvite = require(AppTempCommon.LuaApp.Thunks.ApiSendGameInvite)
|
||||
local ApiFetchPlaceInfos = require(AppTempCommon.LuaApp.Thunks.ApiFetchPlaceInfos)
|
||||
local RetrievalStatus = require(CorePackages.AppTempCommon.LuaApp.Enum.RetrievalStatus)
|
||||
|
||||
local ENTRY_HEIGHT = 62
|
||||
local ENTRY_PADDING = 18
|
||||
|
||||
local NO_RESULTS_FONT = Enum.Font.SourceSans
|
||||
local NO_RESULTS_TEXTCOLOR = Constants.Color.GRAY3
|
||||
local NO_RESULTS_TEXTSIZE = 19
|
||||
local NO_RESULTS_TRANSPRENCY = 0.22
|
||||
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
local PRESENCE_WEIGHTS = {
|
||||
[User.PresenceType.ONLINE] = 3,
|
||||
[User.PresenceType.IN_GAME] = 2,
|
||||
[User.PresenceType.IN_STUDIO] = 1,
|
||||
[User.PresenceType.OFFLINE] = 0,
|
||||
}
|
||||
|
||||
local ConversationList = Roact.PureComponent:extend("ConversationList")
|
||||
|
||||
local function searchFilterPredicate(query, other)
|
||||
if query == "" then
|
||||
return true
|
||||
end
|
||||
return string.find(string.lower(other), query:lower(), 1, true) ~= nil
|
||||
end
|
||||
|
||||
function ConversationList:render()
|
||||
local children = self.props[Roact.Children] or {}
|
||||
local friends = self.props.friends
|
||||
local friendsRetrievalStatus = self.props.friendsRetrievalStatus
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local size = self.props.size
|
||||
local zIndex = self.props.zIndex
|
||||
local topPadding = self.props.topPadding
|
||||
|
||||
local invites = self.props.invites
|
||||
local inviteUser = self.props.inviteUser
|
||||
local searchText = self.props.searchText
|
||||
|
||||
children["RowListLayout"] = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, ENTRY_PADDING),
|
||||
})
|
||||
|
||||
local numEntries = 0
|
||||
-- Populate list of conversations with friends
|
||||
for i, user in ipairs(friends) do
|
||||
local isEntryShown = searchFilterPredicate(searchText, user.name)
|
||||
|
||||
children["User-" .. tostring(i)] = Roact.createElement(ConversationEntry, {
|
||||
visible = isEntryShown,
|
||||
size = UDim2.new(1, 0, 0, ENTRY_HEIGHT),
|
||||
layoutOrder = i,
|
||||
zIndex = zIndex,
|
||||
title = user.name,
|
||||
presence = user.presence,
|
||||
users = {user},
|
||||
inviteUser = inviteUser,
|
||||
inviteStatus = invites[user.id],
|
||||
})
|
||||
|
||||
if isEntryShown then
|
||||
numEntries = numEntries + 1
|
||||
end
|
||||
end
|
||||
|
||||
if #friends == 0 or friendsRetrievalStatus == RetrievalStatus.Fetching then
|
||||
local zeroFriendsComponent = LoadingFriendsPage
|
||||
|
||||
if friendsRetrievalStatus == RetrievalStatus.Done then
|
||||
zeroFriendsComponent = NoFriendsPage
|
||||
elseif friendsRetrievalStatus == RetrievalStatus.Failed then
|
||||
zeroFriendsComponent = FriendsErrorPage
|
||||
end
|
||||
|
||||
return Roact.createElement(zeroFriendsComponent, {
|
||||
BorderSizePixel = 0,
|
||||
LayoutOrder = layoutOrder,
|
||||
Position = UDim2.new(0, 0, 0, topPadding),
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
else
|
||||
if numEntries == 0 then
|
||||
return Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = layoutOrder,
|
||||
Font = NO_RESULTS_FONT,
|
||||
Size = UDim2.new(1, 0, 0, ENTRY_HEIGHT),
|
||||
Text = RobloxTranslator:FormatByKey("Feature.SettingsHub.Label.InviteSearchNoResults"),
|
||||
TextColor3 = NO_RESULTS_TEXTCOLOR,
|
||||
TextSize = NO_RESULTS_TEXTSIZE,
|
||||
TextTransparency = NO_RESULTS_TRANSPRENCY,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return Roact.createElement("ScrollingFrame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = layoutOrder,
|
||||
Size = size,
|
||||
CanvasSize = UDim2.new(0, 0, 0, numEntries * (ENTRY_HEIGHT + ENTRY_PADDING)),
|
||||
ScrollBarThickness = 0,
|
||||
ZIndex = zIndex,
|
||||
}, children)
|
||||
end
|
||||
|
||||
local selectFriends = memoize(function(users)
|
||||
local friends = {}
|
||||
local function friendPreference(friend1, friend2)
|
||||
local friend1Weight = PRESENCE_WEIGHTS[friend1.presence]
|
||||
local friend2Weight = PRESENCE_WEIGHTS[friend2.presence]
|
||||
|
||||
if friend1Weight == friend2Weight then
|
||||
return friend1.name:lower() < friend2.name:lower()
|
||||
else
|
||||
return friend1Weight > friend2Weight
|
||||
end
|
||||
end
|
||||
|
||||
for _, user in pairs(users) do
|
||||
if user.isFriend then
|
||||
friends[#friends + 1] = user
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(friends, friendPreference)
|
||||
|
||||
return friends
|
||||
end)
|
||||
|
||||
-- TODO: Update to use RoactRodux.UNSTABLE_connect2
|
||||
local connector = RoactRodux.connect(function(store, props)
|
||||
local state = store:getState()
|
||||
return {
|
||||
friends = selectFriends(
|
||||
state.Users
|
||||
),
|
||||
friendsRetrievalStatus = state.Friends.retrievalStatus[tostring(Players.LocalPlayer.UserId)],
|
||||
invites = state.Invites,
|
||||
|
||||
inviteUser = function(userId)
|
||||
local requestImpl = httpRequest(HttpRbxApiService)
|
||||
local latestState = store:getState()
|
||||
|
||||
return Promise.new(function(resolve, reject)
|
||||
-- Check that we haven't already invited this user
|
||||
if latestState.Invites[tostring(userId)] == InviteStatus.Pending then
|
||||
reject()
|
||||
else
|
||||
resolve()
|
||||
end
|
||||
end):andThen(function()
|
||||
local placeId = tostring(game.PlaceId)
|
||||
local maybePlaceInfo = latestState.PlaceInfos[placeId]
|
||||
|
||||
-- TODO: This should be a Thunk
|
||||
return Promise.new(function(resolve, reject)
|
||||
-- Log that we've tried inviting this user
|
||||
store:dispatch(ReceivedUserInviteStatus(userId, InviteStatus.Pending))
|
||||
|
||||
if maybePlaceInfo then
|
||||
resolve(maybePlaceInfo)
|
||||
else
|
||||
store:dispatch(ApiFetchPlaceInfos(requestImpl, {placeId})):andThen(function(placeInfos)
|
||||
if placeInfos[1] ~= nil then
|
||||
resolve(placeInfos[1])
|
||||
else
|
||||
reject()
|
||||
end
|
||||
end, function()
|
||||
reject()
|
||||
end)
|
||||
end
|
||||
end):andThen(function(placeInfo)
|
||||
return store:dispatch(ApiSendGameInvite(requestImpl, userId, placeInfo))
|
||||
end):andThen(function(results)
|
||||
store:dispatch(ReceivedUserInviteStatus(userId, results.resultType))
|
||||
end, function()
|
||||
store:dispatch(ReceivedUserInviteStatus(userId, InviteStatus.Failed))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
}
|
||||
end)
|
||||
|
||||
return connector(ConversationList)
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local Constants = require(Modules.Settings.Pages.ShareGame.Constants)
|
||||
|
||||
local FFlagSettingsHubInviteToGameNoBackground = settings():GetFFlag("SettingsHubInviteToGameThumbnailNoBackground")
|
||||
|
||||
local BORDER_SIZE = 1
|
||||
local BORDER_COLOR = Constants.Color.GRAY3
|
||||
|
||||
local THUMBNAIL_IMAGE_SIZE = Constants.InviteAvatarThumbnailSize
|
||||
local THUMBNAIL_IMAGE_TYPE = Constants.InviteAvatarThumbnailType
|
||||
local DEFAULT_THUMBNAIL_ICON = "rbxasset://textures/ui/LuaApp/graphic/ph-avatar-portrait.png"
|
||||
|
||||
-- (Borrowed values from LuaChat)
|
||||
-- This lets us determine how to build the group thumbnail.
|
||||
-- Index represents how many people are in the thumbnail!
|
||||
local IMAGE_LAYOUT = {
|
||||
[1] = {
|
||||
{
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
FrameSize = UDim2.new(1, 0, 1, 0),
|
||||
FramePosition = UDim2.new(0, 0, 0, 0),
|
||||
},
|
||||
},
|
||||
[2] = {
|
||||
{
|
||||
Size = UDim2.new(2, 0, 1, 0),
|
||||
Position = UDim2.new(-0.5, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 1, 0),
|
||||
FramePosition = UDim2.new(0, 0, 0, 0),
|
||||
Border = {
|
||||
BorderPosition = UDim2.new(0.5, -1, 0, 0),
|
||||
BorderSize = UDim2.new(0, 1, 1, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
Size = UDim2.new(2, 0, 1, 0),
|
||||
Position = UDim2.new(-0.5, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 1, 0),
|
||||
FramePosition = UDim2.new(0.5, 1, 0, 0),
|
||||
},
|
||||
},
|
||||
[3] = {
|
||||
{
|
||||
Size = UDim2.new(2, 0, 1, 0),
|
||||
Position = UDim2.new(-0.5, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 1, 0),
|
||||
FramePosition = UDim2.new(0, 0, 0, 0),
|
||||
Border = {
|
||||
BorderPosition = UDim2.new(0.5, -1, 0, 0),
|
||||
BorderSize = UDim2.new(0, 1, 1, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 0.5, -1),
|
||||
FramePosition = UDim2.new(0.5, 1, 0, 0),
|
||||
Border = {
|
||||
BorderPosition = UDim2.new(0.5, 0, 0.5, -1),
|
||||
BorderSize = UDim2.new(0.5, 0, 0, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 0.5, -1),
|
||||
FramePosition = UDim2.new(0.5, 1, 0.5, 1),
|
||||
},
|
||||
},
|
||||
[4] = {
|
||||
{
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 0.5, -1),
|
||||
FramePosition = UDim2.new(0, 0, 0, 0),
|
||||
Border = {
|
||||
BorderPosition = UDim2.new(0, 0, 0.5, -1),
|
||||
BorderSize = UDim2.new(1, 0, 0, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 0.5, -1),
|
||||
FramePosition = UDim2.new(0.5, 1, 0, 0),
|
||||
},
|
||||
{
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 0.5, -1),
|
||||
FramePosition = UDim2.new(0, 0, 0.5, 1),
|
||||
Border = {
|
||||
BorderPosition = UDim2.new(0.5, -1, 0, 0),
|
||||
BorderSize = UDim2.new(0, 1, 1, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
FrameSize = UDim2.new(0.5, -1, 0.5, -1),
|
||||
FramePosition = UDim2.new(0.5, 1, 0.5, 1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local ConversationThumbnail = Roact.PureComponent:extend("ConversationThumbnail")
|
||||
|
||||
function ConversationThumbnail:render()
|
||||
local size = self.props.size
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local zIndex = self.props.zIndex
|
||||
local users = self.props.users
|
||||
local numUsers = #users
|
||||
|
||||
-- Render the thumbnail contents
|
||||
local thumbnailContents = {}
|
||||
for i, user in ipairs(users) do
|
||||
local layoutData = IMAGE_LAYOUT[numUsers][i]
|
||||
|
||||
-- Render the avatar inside a clipping container
|
||||
thumbnailContents["AvatarHolder-"..i] = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
ClipsDescendants = true,
|
||||
Size = layoutData.FrameSize,
|
||||
Position = layoutData.FramePosition,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
Avatar = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = layoutData.Size,
|
||||
Position = layoutData.Position,
|
||||
Image = user and user.thumbnails and user.thumbnails[THUMBNAIL_IMAGE_TYPE]
|
||||
and user.thumbnails[THUMBNAIL_IMAGE_TYPE][THUMBNAIL_IMAGE_SIZE] or DEFAULT_THUMBNAIL_ICON,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
})
|
||||
|
||||
-- Render any borders between avatars
|
||||
if layoutData.Border then
|
||||
thumbnailContents["Border-"..i] = Roact.createElement("Frame", {
|
||||
Size = layoutData.Border.BorderSize,
|
||||
Position = layoutData.Border.BorderPosition,
|
||||
BorderSizePixel = 0,
|
||||
BackgroundColor3 = BORDER_COLOR,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = size,
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
ContentsContainer = Roact.createElement("Frame", {
|
||||
-- Render the border inwards instead of outwards
|
||||
BackgroundTransparency = FFlagSettingsHubInviteToGameNoBackground and 1 or 0,
|
||||
Size = UDim2.new(1, -BORDER_SIZE, 1, -BORDER_SIZE),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
BackgroundColor3 = Constants.Color.WHITE,
|
||||
BorderColor3 = BORDER_COLOR,
|
||||
BorderSizePixel = BORDER_SIZE,
|
||||
ZIndex = zIndex,
|
||||
}, thumbnailContents),
|
||||
})
|
||||
end
|
||||
|
||||
return ConversationThumbnail
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local StoppedToastTimer = require(ShareGame.Actions.StoppedToastTimer)
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
local INNER_TEXT_PADDING = 6
|
||||
local TEXT_SIZE = 16
|
||||
local TIMER_LENGTH = 4
|
||||
|
||||
local ErrorToaster = Roact.PureComponent:extend("ErrorToaster")
|
||||
|
||||
function ErrorToaster:restartTimer()
|
||||
if not self.timerConnection then
|
||||
self.timerConnection =
|
||||
RunService.RenderStepped:Connect(
|
||||
function()
|
||||
local failedInvites = self.props.failedInvites
|
||||
|
||||
if #failedInvites == 0 then
|
||||
self:stopTimer()
|
||||
return
|
||||
end
|
||||
|
||||
local lastModerated = failedInvites[#failedInvites]
|
||||
local goalTick = lastModerated.timeStamp + TIMER_LENGTH
|
||||
|
||||
local now = tick()
|
||||
if now > goalTick then
|
||||
self:stopTimer()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function ErrorToaster:stopTimer()
|
||||
if self.timerConnection then
|
||||
self.timerConnection:Disconnect()
|
||||
self.timerConnection = nil
|
||||
end
|
||||
|
||||
self.props.stoppedTimerDispatch()
|
||||
end
|
||||
|
||||
function ErrorToaster:didUpdate()
|
||||
local failedInvites = self.props.failedInvites
|
||||
|
||||
if #failedInvites ~= 0 then
|
||||
self:restartTimer()
|
||||
end
|
||||
end
|
||||
|
||||
function ErrorToaster:render()
|
||||
local deviceLayout = self.props.deviceLayout
|
||||
local failedInvites = self.props.failedInvites
|
||||
|
||||
local layoutSpecific = Constants.LayoutSpecific[deviceLayout]
|
||||
local toastHeight = layoutSpecific.TOAST_HEIGHT
|
||||
|
||||
if #failedInvites == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local errorMessageKey
|
||||
local inviteStatus = failedInvites[#failedInvites].status
|
||||
if inviteStatus == InviteStatus.Moderated then
|
||||
errorMessageKey = "Feature.SettingsHub.Label.ModeratedInviteError"
|
||||
elseif inviteStatus == InviteStatus.Failed then
|
||||
errorMessageKey = "Feature.SettingsHub.Label.GameInviteError"
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
return Roact.createElement(
|
||||
"ScreenGui",
|
||||
{
|
||||
DisplayOrder = 2,
|
||||
ZIndexBehavior = Enum.ZIndexBehavior.Sibling
|
||||
},
|
||||
{
|
||||
ToastFrame = Roact.createElement(
|
||||
"Frame",
|
||||
{
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundColor3 = Constants.Color.RED,
|
||||
BorderSizePixel = 0,
|
||||
Position = UDim2.new(0.5, 0, 0, 0),
|
||||
Size = UDim2.new(0.5, 0, 0, toastHeight)
|
||||
},
|
||||
{
|
||||
InnerTextPadding = Roact.createElement(
|
||||
"UIPadding",
|
||||
{
|
||||
PaddingLeft = UDim.new(0, INNER_TEXT_PADDING),
|
||||
PaddingRight = UDim.new(0, INNER_TEXT_PADDING)
|
||||
}
|
||||
),
|
||||
ToastText = Roact.createElement(
|
||||
"TextLabel",
|
||||
{
|
||||
BackgroundTransparency = 1,
|
||||
Font = Enum.Font.SourceSansSemibold,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Text = RobloxTranslator:FormatByKey(errorMessageKey),
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
TextSize = TEXT_SIZE,
|
||||
TextWrapped = true
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
local function mapStateToProps(state)
|
||||
return {
|
||||
deviceLayout = state.DeviceInfo.DeviceLayout,
|
||||
failedInvites = state.Toasts.failedInvites
|
||||
}
|
||||
end
|
||||
|
||||
local function mapDispatchToProps(dispatch)
|
||||
return {
|
||||
stoppedTimerDispatch = function()
|
||||
dispatch(StoppedToastTimer())
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(mapStateToProps, mapDispatchToProps)(ErrorToaster)
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local FriendsErrorPage = Roact.PureComponent:extend("FriendsErrorPage")
|
||||
|
||||
function FriendsErrorPage:render()
|
||||
local layoutOrder = self.props.LayoutOrder
|
||||
local zIndex = self.props.ZIndex
|
||||
|
||||
local incrementingLayoutOrder = 0
|
||||
local function incrementLayoutOrder()
|
||||
incrementingLayoutOrder = incrementingLayoutOrder + 1
|
||||
return incrementingLayoutOrder
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
listLayout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
subtitle = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Text = RobloxTranslator:FormatByKey("Feature.SettingsHub.Label.LoadingFriendsListFailed"),
|
||||
TextColor3 = Constants.Color.GRAY5,
|
||||
TextTransparency = 0.22,
|
||||
TextSize = 21,
|
||||
TextWrapped = true,
|
||||
Font = Enum.Font.SourceSans,
|
||||
LayoutOrder = incrementLayoutOrder(),
|
||||
Size = UDim2.new(0, 280, 0, 42),
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return FriendsErrorPage
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local BackButton = require(ShareGame.Components.BackButton)
|
||||
local SearchArea = require(ShareGame.Components.SearchArea)
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local Header = Roact.PureComponent:extend("Header")
|
||||
|
||||
function Header:render()
|
||||
local deviceLayout = self.props.deviceLayout
|
||||
local size = self.props.size
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local zIndex = self.props.zIndex
|
||||
local closePage = self.props.closePage
|
||||
local searchAreaActive = self.props.searchAreaActive
|
||||
|
||||
local layoutSpecific = Constants.LayoutSpecific[deviceLayout]
|
||||
local isDesktop = deviceLayout == Constants.DeviceLayout.DESKTOP
|
||||
local isSearchingOnMobile = (not isDesktop) and searchAreaActive
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = size,
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
Title = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Visible = not isSearchingOnMobile,
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
Text = RobloxTranslator:FormatByKey("Feature.SettingsHub.Heading.InviteFriends"),
|
||||
TextSize = layoutSpecific.PAGE_TITLE_TEXT_SIZE,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
Font = Enum.Font.SourceSansSemibold,
|
||||
ZIndex = self.props.zIndex,
|
||||
}),
|
||||
BackButton = Roact.createElement(BackButton, {
|
||||
isArrow = not isDesktop,
|
||||
visible = not isSearchingOnMobile,
|
||||
position = UDim2.new(0, 0, 0.5, 0),
|
||||
size = UDim2.new(
|
||||
0, layoutSpecific.BACK_BUTTON_WIDTH,
|
||||
0, layoutSpecific.BACK_BUTTON_HEIGHT
|
||||
),
|
||||
anchorPoint = Vector2.new(0, 0.5),
|
||||
zIndex = zIndex,
|
||||
onClick = closePage,
|
||||
}),
|
||||
SearchArea = Roact.createElement(SearchArea, {
|
||||
fullWidthSearchBar = not isDesktop,
|
||||
searchBoxMargin = layoutSpecific.SEARCH_BOX_MARGIN,
|
||||
anchorPoint = Vector2.new(1, 0.5),
|
||||
position = UDim2.new(1, 0, 0.5, 0),
|
||||
zIndex = zIndex,
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return Header
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
|
||||
local IconButton = Roact.PureComponent:extend("IconButton")
|
||||
|
||||
function IconButton:render()
|
||||
local visible = self.props.visible
|
||||
local anchorPoint = self.props.anchorPoint
|
||||
local position = self.props.position
|
||||
local size = self.props.size
|
||||
local zIndex = self.props.zIndex
|
||||
local onClick = self.props.onClick
|
||||
|
||||
local iconHorizontalAlignment = self.props.iconHorizontalAlignment or Enum.HorizontalAlignment.Center
|
||||
local iconVerticalAlignment = self.props.iconVerticalAlignment or Enum.VerticalAlignment.Center
|
||||
local iconSpritePath = self.props.iconSpritePath
|
||||
local iconSpriteFrame = self.props.iconSpriteFrame
|
||||
|
||||
local horizontalInset = self.props.horizontalInset or 12
|
||||
local offsetLayoutOrder = 0
|
||||
local iconLayoutOrder = 1
|
||||
local iconSize = UDim2.new(0, iconSpriteFrame.size.X, 0, iconSpriteFrame.size.Y)
|
||||
|
||||
if iconHorizontalAlignment == Enum.HorizontalAlignment.Center then
|
||||
horizontalInset = 0
|
||||
elseif iconHorizontalAlignment == Enum.HorizontalAlignment.Left then
|
||||
position = UDim2.new(position.X.Scale, position.X.Offset - horizontalInset, position.Y.Scale, position.Y.Offset)
|
||||
elseif iconHorizontalAlignment == Enum.HorizontalAlignment.Right then
|
||||
position = UDim2.new(position.X.Scale, position.X.Offset + horizontalInset, position.Y.Scale, position.Y.Offset)
|
||||
|
||||
offsetLayoutOrder = 1
|
||||
iconLayoutOrder = 0
|
||||
end
|
||||
|
||||
size = UDim2.new(size.X.Scale, size.X.Offset + horizontalInset, size.Y.Scale, size.Y.Offset)
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
Visible = visible,
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
AnchorPoint = anchorPoint,
|
||||
Position = position,
|
||||
Size = size,
|
||||
ZIndex = zIndex,
|
||||
[Roact.Event.Activated] = onClick,
|
||||
}, {
|
||||
IconLayout = Roact.createElement("UIListLayout", {
|
||||
HorizontalAlignment = iconHorizontalAlignment,
|
||||
VerticalAlignment = iconVerticalAlignment,
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
Offset = Roact.createElement("Frame", {
|
||||
BorderSizePixel = 0,
|
||||
|
||||
LayoutOrder = offsetLayoutOrder,
|
||||
Size = UDim2.new(0, horizontalInset, 0, 0),
|
||||
}),
|
||||
BackIcon = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
Size = UDim2.new(0, iconSpriteFrame.size.X, 0, iconSpriteFrame.size.Y),
|
||||
|
||||
Image = iconSpritePath,
|
||||
ImageRectOffset = iconSpriteFrame.offset,
|
||||
ImageRectSize = iconSpriteFrame.size,
|
||||
ZIndex = zIndex,
|
||||
LayoutOrder = iconLayoutOrder,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return IconButton
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
local Immutable = require(AppTempCommon.Common.Immutable)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local RectangleButton = require(ShareGame.Components.RectangleButton)
|
||||
|
||||
local INVITE_TEXT_FONT = Enum.Font.SourceSansSemibold
|
||||
local INVITE_TEXT_SIZE = 19
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
local INVITE_STATUS_TEXT = {
|
||||
[InviteStatus.Success] = "Feature.SettingsHub.Label.Invited",
|
||||
[InviteStatus.Moderated] = "Feature.SettingsHub.Label.Moderated",
|
||||
[InviteStatus.Pending] = "Feature.SettingsHub.Label.Sending",
|
||||
}
|
||||
|
||||
local InviteButton = Roact.PureComponent:extend("InviteButton")
|
||||
|
||||
function InviteButton:render()
|
||||
local anchorPoint = self.props.anchorPoint
|
||||
local position = self.props.position
|
||||
local size = self.props.size
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local zIndex = self.props.zIndex
|
||||
local onInvite = self.props.onInvite
|
||||
|
||||
local inviteStatus = self.props.inviteStatus
|
||||
|
||||
if not inviteStatus or inviteStatus == InviteStatus.Failed then
|
||||
local buttonProps = Immutable.Set(self.props, "onClick", function()
|
||||
onInvite()
|
||||
end)
|
||||
|
||||
return Roact.createElement(RectangleButton, buttonProps, {
|
||||
InviteText = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
Font = INVITE_TEXT_FONT,
|
||||
Text = RobloxTranslator:FormatByKey("Feature.SettingsHub.Action.InviteFriend"),
|
||||
TextSize = INVITE_TEXT_SIZE,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
})
|
||||
else
|
||||
local inviteText = INVITE_STATUS_TEXT[inviteStatus]
|
||||
|
||||
return Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
AnchorPoint = anchorPoint,
|
||||
Position = position,
|
||||
Size = size,
|
||||
Font = INVITE_TEXT_FONT,
|
||||
Text = RobloxTranslator:FormatByKey(inviteText),
|
||||
TextSize = INVITE_TEXT_SIZE,
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return InviteButton
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local SetDeviceOrientation = require(AppTempCommon.LuaApp.Actions.SetDeviceOrientation)
|
||||
local SetIsSmallTouchScreen = require(ShareGame.Actions.SetIsSmallTouchScreen)
|
||||
local SetDeviceLayout = require(ShareGame.Actions.SetDeviceLayout)
|
||||
|
||||
-- Magic values derived from CoreScripts/Modules/Settings/Utility.lua
|
||||
local SMALL_DEVICE_HEIGHT = 500
|
||||
local SMALL_DEVICE_WIDTH = 700
|
||||
|
||||
local LayoutProvider = Roact.Component:extend("LayoutProvider")
|
||||
|
||||
function LayoutProvider:didMount()
|
||||
if workspace.CurrentCamera then
|
||||
self:setObservedCamera(workspace.CurrentCamera)
|
||||
end
|
||||
|
||||
self.cameraChangedListener = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
|
||||
if workspace.CurrentCamera then
|
||||
self:setObservedCamera(workspace.CurrentCamera)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function LayoutProvider:willUnmount()
|
||||
if self.cameraChangedListener then
|
||||
self.cameraChangedListener:Disconnect()
|
||||
end
|
||||
|
||||
if self.viewportSizeListener then
|
||||
self.viewportSizeListener:Disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
function LayoutProvider:setObservedCamera(camera)
|
||||
if self.viewportSizeListener then
|
||||
self.viewportSizeListener:Disconnect()
|
||||
end
|
||||
|
||||
-- Listen for changes to ViewportSize and update DeviceInfo accordingly
|
||||
self:checkAllDeviceInfo(camera.ViewportSize)
|
||||
self.viewportSizeListener = camera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
|
||||
-- Hacky code awaits underlying mechanism fix.
|
||||
-- Viewport will get a 0,0,1,1 rect before it is properly set.
|
||||
local viewportSize = camera.ViewportSize
|
||||
if viewportSize.X <= 1 or viewportSize.Y <= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
self:checkAllDeviceInfo(viewportSize)
|
||||
end)
|
||||
end
|
||||
|
||||
function LayoutProvider:checkDeviceOrientation(viewportSize)
|
||||
local deviceOrientation = viewportSize.X > viewportSize.Y and
|
||||
Constants.DeviceOrientation.LANDSCAPE or Constants.DeviceOrientation.PORTRAIT
|
||||
|
||||
if self._deviceOrientation ~= deviceOrientation then
|
||||
self._deviceOrientation = deviceOrientation
|
||||
self.props.setDeviceOrientation(deviceOrientation)
|
||||
end
|
||||
end
|
||||
|
||||
function LayoutProvider:checkDeviceIsSmallTouchScreen(viewportSize)
|
||||
local isSmallTouchScreen = UserInputService.TouchEnabled and
|
||||
(viewportSize.X < SMALL_DEVICE_WIDTH or viewportSize.Y < SMALL_DEVICE_HEIGHT)
|
||||
|
||||
if self._isSmallTouchScreen ~= isSmallTouchScreen then
|
||||
self._isSmallTouchScreen = isSmallTouchScreen
|
||||
self.props.setIsSmallTouchScreen(self._isSmallTouchScreen)
|
||||
end
|
||||
end
|
||||
|
||||
function LayoutProvider:checkDeviceLayout()
|
||||
local deviceLayout
|
||||
|
||||
if self._isSmallTouchScreen then
|
||||
if self._deviceOrientation == Constants.DeviceOrientation.LANDSCAPE then
|
||||
deviceLayout = Constants.DeviceLayout.PHONE_LANDSCAPE
|
||||
else
|
||||
deviceLayout = Constants.DeviceLayout.PHONE_PORTRAIT
|
||||
end
|
||||
elseif UserInputService.TouchEnabled then
|
||||
if self._deviceOrientation == Constants.DeviceOrientation.LANDSCAPE then
|
||||
deviceLayout = Constants.DeviceLayout.TABLET_LANDSCAPE
|
||||
else
|
||||
deviceLayout = Constants.DeviceLayout.TABLET_PORTRAIT
|
||||
end
|
||||
else
|
||||
deviceLayout = Constants.DeviceLayout.DESKTOP
|
||||
end
|
||||
|
||||
if self._deviceLayout ~= deviceLayout then
|
||||
self._deviceLayout = deviceLayout
|
||||
self.props.setDeviceLayout(self._deviceLayout)
|
||||
end
|
||||
end
|
||||
|
||||
function LayoutProvider:checkAllDeviceInfo(viewportSize)
|
||||
self:checkDeviceOrientation(viewportSize)
|
||||
self:checkDeviceIsSmallTouchScreen(viewportSize)
|
||||
self:checkDeviceLayout()
|
||||
end
|
||||
|
||||
function LayoutProvider:render()
|
||||
return Roact.oneChild(self.props[Roact.Children])
|
||||
end
|
||||
|
||||
-- TODO: Update to use RoactRodux.UNSTABLE_connect2
|
||||
local connector = RoactRodux.connect(function(store)
|
||||
return {
|
||||
setDeviceOrientation = function(orientation)
|
||||
return store:dispatch(SetDeviceOrientation(orientation))
|
||||
end,
|
||||
setIsSmallTouchScreen = function(isSmall)
|
||||
return store:dispatch(SetIsSmallTouchScreen(isSmall))
|
||||
end,
|
||||
setDeviceLayout = function(deviceLayout)
|
||||
return store:dispatch(SetDeviceLayout(deviceLayout))
|
||||
end,
|
||||
}
|
||||
end)
|
||||
|
||||
return connector(LayoutProvider)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local LoadingBar = require(CorePackages.AppTempCommon.LuaApp.Components.LoadingBar)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local LoadingFriendsPage = Roact.PureComponent:extend("LoadingFriendsPage")
|
||||
|
||||
local LOADING_INDICATOR_WIDTH = 70
|
||||
local LOADING_INDICATOR_HEIGHT = 16
|
||||
|
||||
function LoadingFriendsPage:render()
|
||||
local layoutOrder = self.props.LayoutOrder
|
||||
local zIndex = self.props.ZIndex
|
||||
local scale = 1
|
||||
|
||||
local incrementingLayoutOrder = 0
|
||||
local function incrementLayoutOrder()
|
||||
incrementingLayoutOrder = incrementingLayoutOrder + 1
|
||||
return incrementingLayoutOrder
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
listLayout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
loadingIndicator = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, scale * LOADING_INDICATOR_WIDTH, 0, scale * LOADING_INDICATOR_HEIGHT),
|
||||
LayoutOrder = incrementLayoutOrder(),
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
loadingBar = Roact.createElement(LoadingBar, {
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return LoadingFriendsPage
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local ShareGameIcons = require(ShareGame.Spritesheets.ShareGameIcons)
|
||||
local FRIENDS_ICON_FRAME = ShareGameIcons:GetFrame("friends")
|
||||
local SHARE_GAME_ICONS_IMAGE = ShareGameIcons:GetImagePath()
|
||||
|
||||
local ICON_TO_SUBTITLE_PADDING = 34
|
||||
|
||||
local NoFriendsPage = Roact.PureComponent:extend("NoFriendsPage")
|
||||
|
||||
function NoFriendsPage:render()
|
||||
local layoutOrder = self.props.LayoutOrder
|
||||
local zIndex = self.props.ZIndex
|
||||
|
||||
local incrementingLayoutOrder = 0
|
||||
local function incrementLayoutOrder()
|
||||
incrementingLayoutOrder = incrementingLayoutOrder + 1
|
||||
return incrementingLayoutOrder
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
listLayout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
friendsIcon = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Image = SHARE_GAME_ICONS_IMAGE,
|
||||
ImageRectOffset = FRIENDS_ICON_FRAME.offset,
|
||||
ImageRectSize = FRIENDS_ICON_FRAME.size,
|
||||
Size = UDim2.new(0, 72, 0, 72),
|
||||
LayoutOrder = incrementLayoutOrder(),
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
iconToSubtitleSpacer = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, 0, 0, ICON_TO_SUBTITLE_PADDING),
|
||||
LayoutOrder = incrementLayoutOrder(),
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
subtitle = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Text = RobloxTranslator:FormatByKey("Feature.SettingsHub.Label.NoFriendsScreen"),
|
||||
TextColor3 = Constants.Color.GRAY5,
|
||||
TextTransparency = 0.22,
|
||||
TextSize = 21,
|
||||
TextWrapped = true,
|
||||
Font = Enum.Font.SourceSans,
|
||||
LayoutOrder = incrementLayoutOrder(),
|
||||
Size = UDim2.new(0, 280, 0, 42),
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
bottomSpacer = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, 0, 0, 120),
|
||||
LayoutOrder = incrementLayoutOrder(),
|
||||
ZIndex = zIndex,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return NoFriendsPage
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local BUTTON_IMAGE = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButton.png"
|
||||
local BUTTON_IMAGE_ACTIVE = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButtonSelected.png"
|
||||
local BUTTON_SLICE = Rect.new(8, 6, 46, 44)
|
||||
|
||||
local DROPSHADOW_SIZE = {
|
||||
Left = 4, Right = 4,
|
||||
Top = 2, Bottom = 6,
|
||||
}
|
||||
|
||||
local RectangleButton = Roact.PureComponent:extend("RectangleButton")
|
||||
|
||||
function RectangleButton:init()
|
||||
self.state = {
|
||||
isHovering = false,
|
||||
}
|
||||
end
|
||||
|
||||
function RectangleButton:render()
|
||||
local size = self.props.size
|
||||
local position = self.props.position
|
||||
local anchorPoint = self.props.anchorPoint
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
local zIndex = self.props.zIndex
|
||||
local onClick = self.props.onClick
|
||||
local children = self.props[Roact.Children] or {}
|
||||
|
||||
local buttonImage = self.state.isHovering and BUTTON_IMAGE_ACTIVE or BUTTON_IMAGE
|
||||
|
||||
-- Insert padding so that child elements of this component are positioned
|
||||
-- inside the button as expected. This is to offset the dropshadow
|
||||
-- extending outside the button bounds.
|
||||
children["UIPadding"] = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, DROPSHADOW_SIZE.Left),
|
||||
PaddingRight = UDim.new(0, DROPSHADOW_SIZE.Right),
|
||||
PaddingTop = UDim.new(0, DROPSHADOW_SIZE.Top),
|
||||
PaddingBottom = UDim.new(0, DROPSHADOW_SIZE.Bottom),
|
||||
})
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
BackgroundTransparency = 1,
|
||||
Image = "",
|
||||
Size = size,
|
||||
Position = position,
|
||||
AnchorPoint = anchorPoint,
|
||||
LayoutOrder = layoutOrder,
|
||||
ZIndex = zIndex,
|
||||
|
||||
[Roact.Event.InputBegan] = function()
|
||||
self:setState({isHovering = true})
|
||||
end,
|
||||
[Roact.Event.InputEnded] = function()
|
||||
self:setState({isHovering = false})
|
||||
end,
|
||||
|
||||
[Roact.Event.Activated] = function()
|
||||
if onClick then
|
||||
self:setState({isHovering = false})
|
||||
onClick()
|
||||
end
|
||||
end,
|
||||
}, {
|
||||
ButtonBackground = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(
|
||||
0, -DROPSHADOW_SIZE.Left,
|
||||
0, -DROPSHADOW_SIZE.Top
|
||||
),
|
||||
Size = UDim2.new(
|
||||
1, DROPSHADOW_SIZE.Left + DROPSHADOW_SIZE.Right,
|
||||
1, DROPSHADOW_SIZE.Top + DROPSHADOW_SIZE.Bottom
|
||||
),
|
||||
Image = buttonImage,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = BUTTON_SLICE,
|
||||
ZIndex = zIndex,
|
||||
}, children),
|
||||
})
|
||||
end
|
||||
|
||||
return RectangleButton
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
local SearchBox = require(ShareGame.Components.SearchBox)
|
||||
local IconButton = require(ShareGame.Components.IconButton)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local Text = require(AppTempCommon.Common.Text)
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local SetSearchAreaActive = require(ShareGame.Actions.SetSearchAreaActive)
|
||||
local SetSearchText = require(ShareGame.Actions.SetSearchText)
|
||||
local ShareGameIcons = require(RobloxGui.Modules.Settings.Pages.ShareGame.Spritesheets.ShareGameIcons)
|
||||
|
||||
local SEARCH_ICON_SPRITE_PATH = ShareGameIcons:GetImagePath()
|
||||
local SEARCH_ICON_SPRITE_FRAME = ShareGameIcons:GetFrame("search_large")
|
||||
local SEARCH_ICON_SIZE = 44
|
||||
|
||||
local SEARCH_BOX_WIDTH = 177
|
||||
local SEARCH_BOX_HEIGHT = 28
|
||||
|
||||
local CANCEL_TEXT_FONT = Enum.Font.SourceSans
|
||||
local CANCEL_TEXT_COLOR = Constants.Color.GRAY3
|
||||
local CANCEL_TEXT_SIZE = 20
|
||||
|
||||
local SearchArea = Roact.PureComponent:extend("SearchArea")
|
||||
|
||||
function SearchArea:init()
|
||||
self.state = {
|
||||
cancelText = RobloxTranslator:FormatByKey("Feature.SettingsHub.Action.CancelSearch"),
|
||||
}
|
||||
self.searchField = nil
|
||||
end
|
||||
|
||||
function SearchArea:render()
|
||||
local fullWidthSearchBar = self.props.fullWidthSearchBar
|
||||
local searchBoxMargin = self.props.searchBoxMargin
|
||||
local anchorPoint = self.props.anchorPoint
|
||||
local position = self.props.position
|
||||
local zIndex = self.props.zIndex
|
||||
local searchAreaActive = self.props.searchAreaActive
|
||||
|
||||
local cancelText = self.state.cancelText
|
||||
local cancelTextWidth = Text.GetTextWidth(cancelText, CANCEL_TEXT_FONT, CANCEL_TEXT_SIZE)
|
||||
local searchBoxSizeOffset = searchBoxMargin + cancelTextWidth
|
||||
|
||||
local setSearchText = self.props.setSearchText
|
||||
|
||||
if fullWidthSearchBar then
|
||||
-- When full-width is true, the search box spans the entire width of the
|
||||
-- parent frame, and it becomes hidden behind a search button.
|
||||
return Roact.createElement("Frame", {
|
||||
AnchorPoint = anchorPoint,
|
||||
Position = position,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
-- Render the search button if the search area hasn't been activated
|
||||
-- yet.
|
||||
SearchButton = Roact.createElement(IconButton, {
|
||||
visible = not searchAreaActive,
|
||||
anchorPoint = anchorPoint,
|
||||
position = position,
|
||||
size = UDim2.new(0, SEARCH_ICON_SIZE, 0, SEARCH_ICON_SIZE),
|
||||
zIndex = zIndex,
|
||||
|
||||
iconHorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||
iconSpritePath = SEARCH_ICON_SPRITE_PATH,
|
||||
iconSpriteFrame = SEARCH_ICON_SPRITE_FRAME,
|
||||
onClick = function(rbx)
|
||||
self.props.setSearchAreaActive(true)
|
||||
end,
|
||||
}),
|
||||
|
||||
-- Show search box with cancel button if the search area is active
|
||||
SearchBox = Roact.createElement(SearchBox, {
|
||||
anchorPoint = Vector2.new(0, 0.5),
|
||||
position = UDim2.new(0, 0, 0.5, 0),
|
||||
size = UDim2.new(1, -searchBoxSizeOffset, 0, SEARCH_BOX_HEIGHT),
|
||||
zIndex = zIndex,
|
||||
visible = searchAreaActive,
|
||||
modalFocused = searchAreaActive,
|
||||
onTextChanged = setSearchText,
|
||||
onTextBoxFocusLost = function(text)
|
||||
if text == "" then
|
||||
self.props.setSearchAreaActive(false)
|
||||
end
|
||||
end,
|
||||
searchFieldRef = function(rbx)
|
||||
self.searchField = rbx
|
||||
end,
|
||||
}),
|
||||
Cancel = Roact.createElement("TextButton", {
|
||||
BackgroundTransparency = 1,
|
||||
AnchorPoint = Vector2.new(1, 0.5),
|
||||
Position = UDim2.new(1, 0, 0.5, 0),
|
||||
Size = UDim2.new(0, cancelTextWidth, 1, 0),
|
||||
TextSize = CANCEL_TEXT_SIZE,
|
||||
TextColor3 = CANCEL_TEXT_COLOR,
|
||||
Font = CANCEL_TEXT_FONT,
|
||||
ZIndex = zIndex,
|
||||
Visible = searchAreaActive,
|
||||
|
||||
[Roact.Ref] = function(rbx)
|
||||
if rbx then
|
||||
-- Set initial text on this TextLabel
|
||||
rbx.Text = cancelText
|
||||
|
||||
-- Listen to Text changed events in case of localization
|
||||
-- (this is so we can trigger a resize)
|
||||
if not self.textConnection then
|
||||
local textChangedSignal = rbx:GetPropertyChangedSignal("Text")
|
||||
|
||||
self.textConnection = textChangedSignal:connect(function()
|
||||
self:setState({
|
||||
cancelText = rbx.Text
|
||||
})
|
||||
end)
|
||||
end
|
||||
end
|
||||
end,
|
||||
[Roact.Event.Activated] = function(rbx)
|
||||
self.props.setSearchAreaActive(false)
|
||||
end,
|
||||
}),
|
||||
})
|
||||
else
|
||||
-- Show a search box of a fixed size without any added behavior.
|
||||
-- We don't bother setting SearchAreaActive here since we're not working
|
||||
-- with a modal-style search box (i.e. nothing has to be shown or
|
||||
-- hidden).
|
||||
return Roact.createElement(SearchBox, {
|
||||
anchorPoint = Vector2.new(1, 0.5),
|
||||
position = UDim2.new(1, 0, 0.5, 0),
|
||||
size = UDim2.new(0, SEARCH_BOX_WIDTH, 0, SEARCH_BOX_HEIGHT),
|
||||
zIndex = zIndex,
|
||||
onTextChanged = setSearchText,
|
||||
searchFieldRef = function(rbx)
|
||||
self.searchField = rbx
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function SearchArea:didUpdate(prevProps)
|
||||
local fullWidthSearchBar = self.props.fullWidthSearchBar
|
||||
|
||||
if self.searchField then
|
||||
-- Check if the page was closed so we can reset the search query
|
||||
if not self.props.isPageOpen and prevProps.isPageOpen then
|
||||
-- Spawn new thread so we don't trigger state updates from this
|
||||
-- function
|
||||
spawn(function()
|
||||
-- Reset the entire search area
|
||||
if fullWidthSearchBar then
|
||||
self.props.setSearchAreaActive(false)
|
||||
else
|
||||
self.searchField.Text = ""
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Check if the search area has become active/inactive
|
||||
if fullWidthSearchBar then
|
||||
local searchAreaActive = self.props.searchAreaActive
|
||||
local wasActive = prevProps.searchAreaActive
|
||||
|
||||
if searchAreaActive and not wasActive then
|
||||
self.searchField:CaptureFocus()
|
||||
elseif not searchAreaActive and wasActive then
|
||||
self.searchField.Text = ""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Update to use RoactRodux.UNSTABLE_connect2
|
||||
SearchArea = RoactRodux.connect(function(store)
|
||||
local state = store:getState()
|
||||
return {
|
||||
isPageOpen = state.Page.IsOpen,
|
||||
searchAreaActive = state.ConversationsSearch.SearchAreaActive,
|
||||
setSearchAreaActive = function(isActive)
|
||||
store:dispatch(SetSearchAreaActive(isActive))
|
||||
end,
|
||||
setSearchText = function(text)
|
||||
store:dispatch(SetSearchText(text))
|
||||
end,
|
||||
}
|
||||
end)(SearchArea)
|
||||
|
||||
return SearchArea
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Constants = require(RobloxGui.Modules.Settings.Pages.ShareGame.Constants)
|
||||
|
||||
local SearchBox = Roact.Component:extend("SearchBox")
|
||||
|
||||
local ShareGameIcons = require(RobloxGui.Modules.Settings.Pages.ShareGame.Spritesheets.ShareGameIcons)
|
||||
local SHARE_GAME_SPRITE_PATH = ShareGameIcons:GetImagePath()
|
||||
|
||||
local SEARCH_BORDER_SPRITE_IMAGE = SHARE_GAME_SPRITE_PATH
|
||||
local SEARCH_BORDER_SPRITE_FRAME = ShareGameIcons:GetFrame("search_border")
|
||||
local SEARCH_BORDER_SLICE = Rect.new(3, 3, 4, 4)
|
||||
|
||||
local SEARCH_ICON_SPRITE_IMAGE = SHARE_GAME_SPRITE_PATH
|
||||
local SEARCH_ICON_SPRITE_FRAME = ShareGameIcons:GetFrame("search_small")
|
||||
local SEARCH_ICON_SIZE = 16
|
||||
|
||||
local CLEAR_ICON_SPRITE_IMAGE = SHARE_GAME_SPRITE_PATH
|
||||
local CLEAR_ICON_SPRITE_FRAME = ShareGameIcons:GetFrame("clear")
|
||||
local CLEAR_ICON_SIZE = 16
|
||||
|
||||
local SEARCH_MARGINS_HORIZONTAL = 8
|
||||
local SEARCH_MARGINS_VERTICAL = 6
|
||||
local SEARCH_FIELD_MARGINS = 12
|
||||
|
||||
local SEARCH_BOX_TEXT_SIZE = 16
|
||||
|
||||
function SearchBox:init()
|
||||
self.state = {
|
||||
isTextWritten = false,
|
||||
}
|
||||
self.searchField = nil
|
||||
end
|
||||
|
||||
function SearchBox:render()
|
||||
local anchorPoint = self.props.anchorPoint
|
||||
local onTextBoxFocusLost = self.props.onTextBoxFocusLost
|
||||
local position = self.props.position
|
||||
local searchFieldRef = self.props.searchFieldRef
|
||||
local size = self.props.size
|
||||
local visible = self.props.visible
|
||||
local zIndex = self.props.zIndex
|
||||
|
||||
|
||||
local isTextWritten = self.state.isTextWritten
|
||||
return Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
AnchorPoint = anchorPoint,
|
||||
Position = position,
|
||||
Size = size,
|
||||
Visible = visible,
|
||||
Image = SEARCH_BORDER_SPRITE_IMAGE,
|
||||
ImageRectOffset = SEARCH_BORDER_SPRITE_FRAME.offset,
|
||||
ImageRectSize = SEARCH_BORDER_SPRITE_FRAME.size,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = SEARCH_BORDER_SLICE,
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
UIPadding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, SEARCH_MARGINS_HORIZONTAL),
|
||||
PaddingRight = UDim.new(0, SEARCH_MARGINS_HORIZONTAL),
|
||||
PaddingTop = UDim.new(0, SEARCH_MARGINS_VERTICAL),
|
||||
PaddingBottom = UDim.new(0, SEARCH_MARGINS_VERTICAL),
|
||||
}),
|
||||
SearchIcon = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Image = SEARCH_ICON_SPRITE_IMAGE,
|
||||
ImageRectOffset = SEARCH_ICON_SPRITE_FRAME.offset,
|
||||
ImageRectSize = SEARCH_ICON_SPRITE_FRAME.size,
|
||||
Size = UDim2.new(0, SEARCH_ICON_SIZE, 0, SEARCH_ICON_SIZE),
|
||||
ZIndex = zIndex
|
||||
}),
|
||||
SearchField = Roact.createElement("TextBox", {
|
||||
BackgroundTransparency = 1,
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Position = UDim2.new(0, SEARCH_ICON_SIZE + SEARCH_FIELD_MARGINS, 0.5, 0),
|
||||
Size = UDim2.new(
|
||||
1, -(SEARCH_ICON_SIZE + CLEAR_ICON_SIZE + SEARCH_FIELD_MARGINS * 2),
|
||||
1, 0
|
||||
),
|
||||
ClearTextOnFocus = false,
|
||||
PlaceholderColor3 = Constants.Color.GREY3,
|
||||
PlaceholderText = RobloxTranslator:FormatByKey("Feature.SettingsHub.Label.SearchForFriendsPlaceholder"),
|
||||
Text = "",
|
||||
TextColor3 = Constants.Color.WHITE,
|
||||
TextWrapped = true,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextSize = SEARCH_BOX_TEXT_SIZE,
|
||||
Font = Enum.Font.SourceSans,
|
||||
ZIndex = zIndex,
|
||||
[Roact.Ref] = function(rbx)
|
||||
self.searchField = rbx
|
||||
|
||||
-- Check if a higher reference function has been passed in and
|
||||
-- then call it.
|
||||
if searchFieldRef then
|
||||
searchFieldRef(rbx)
|
||||
end
|
||||
end,
|
||||
[Roact.Event.FocusLost] = function(rbx)
|
||||
if onTextBoxFocusLost then
|
||||
onTextBoxFocusLost(rbx.Text)
|
||||
end
|
||||
end
|
||||
}),
|
||||
ClearButton = Roact.createElement("ImageButton", {
|
||||
BackgroundTransparency = 1,
|
||||
AnchorPoint = Vector2.new(1, 0),
|
||||
Position = UDim2.new(1, 0, 0, 0),
|
||||
Size = UDim2.new(0, CLEAR_ICON_SIZE, 0, SEARCH_ICON_SIZE),
|
||||
Image = CLEAR_ICON_SPRITE_IMAGE,
|
||||
ImageRectOffset = CLEAR_ICON_SPRITE_FRAME.offset,
|
||||
ImageRectSize = CLEAR_ICON_SPRITE_FRAME.size,
|
||||
ZIndex = zIndex,
|
||||
Visible = isTextWritten,
|
||||
[Roact.Event.Activated] = function()
|
||||
if self.searchField then
|
||||
self.searchField.Text = ""
|
||||
|
||||
-- We lost focus when clicking this button so we have to
|
||||
-- recapture it.
|
||||
self.searchField:CaptureFocus()
|
||||
end
|
||||
end
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
function SearchBox:didMount()
|
||||
local searchField = self.searchField
|
||||
local onTextChanged = self.props.onTextChanged
|
||||
|
||||
if searchField then
|
||||
self.textBoxChangedConnection = searchField:GetPropertyChangedSignal("Text"):Connect(function()
|
||||
local text = searchField.Text
|
||||
self:setState({
|
||||
isTextWritten = text:len() > 0
|
||||
})
|
||||
onTextChanged(text)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function SearchBox:willUnmount()
|
||||
if self.textBoxChangedConnection then
|
||||
self.textBoxChangedConnection:Disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
return SearchBox
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
--[[
|
||||
Container for both the Share Game Page contents and header
|
||||
]]
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local HttpRbxApiService = game:GetService("HttpRbxApiService")
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
local Modules = CoreGui.RobloxGui.Modules
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local httpRequest = require(AppTempCommon.Temp.httpRequest)
|
||||
local ApiFetchUsersFriends = require(AppTempCommon.LuaApp.Thunks.ApiFetchUsersFriends)
|
||||
local RetrievalStatus = require(CorePackages.AppTempCommon.LuaApp.Enum.RetrievalStatus)
|
||||
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Header = require(ShareGame.Components.Header)
|
||||
local ConversationList = require(ShareGame.Components.ConversationList)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local ClosePage = require(ShareGame.Actions.ClosePage)
|
||||
|
||||
local USER_LIST_PADDING = 10
|
||||
|
||||
local ShareGamePageFrame = Roact.PureComponent:extend("ShareGamePageFrame")
|
||||
|
||||
local ToasterComponent = require(ShareGame.Components.ErrorToaster)
|
||||
|
||||
function ShareGamePageFrame:init()
|
||||
self.props.reFetch()
|
||||
end
|
||||
|
||||
function ShareGamePageFrame:render()
|
||||
local deviceLayout = self.props.deviceLayout
|
||||
local zIndex = self.props.zIndex
|
||||
local closePage = self.props.closePage
|
||||
local searchAreaActive = self.props.searchAreaActive
|
||||
local searchText = self.props.searchText
|
||||
|
||||
local layoutSpecific = Constants.LayoutSpecific[deviceLayout]
|
||||
local headerHeight = layoutSpecific.HEADER_HEIGHT
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
ZIndex = zIndex,
|
||||
}, {
|
||||
toasterPortal = Roact.createElement(Roact.Portal, {
|
||||
target = CoreGui,
|
||||
}, {
|
||||
Toaster = Roact.createElement(ToasterComponent),
|
||||
}),
|
||||
|
||||
Header = Roact.createElement(Header, {
|
||||
deviceLayout = deviceLayout,
|
||||
size = UDim2.new(1, 0, 0, headerHeight),
|
||||
position = UDim2.new(0, 0, 0, -headerHeight),
|
||||
layoutOrder = 0,
|
||||
zIndex = zIndex,
|
||||
closePage = closePage,
|
||||
searchAreaActive = searchAreaActive,
|
||||
}),
|
||||
ConversationList = Roact.createElement(ConversationList, {
|
||||
size = UDim2.new(1, 0, 1, layoutSpecific.EXTEND_BOTTOM_SIZE - USER_LIST_PADDING),
|
||||
topPadding = USER_LIST_PADDING,
|
||||
layoutOrder = 1,
|
||||
zIndex = zIndex,
|
||||
searchText = searchText,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
-- TODO: Update to use RoactRodux.UNSTABLE_connect2
|
||||
ShareGamePageFrame = RoactRodux.connect(function(store)
|
||||
local state = store:getState()
|
||||
return {
|
||||
deviceLayout = state.DeviceInfo.DeviceLayout,
|
||||
|
||||
searchAreaActive = state.ConversationsSearch.SearchAreaActive,
|
||||
searchText = state.ConversationsSearch.SearchText,
|
||||
|
||||
closePage = function()
|
||||
store:dispatch(ClosePage(Constants.PageRoute.SHARE_GAME))
|
||||
end,
|
||||
|
||||
reFetch = function()
|
||||
local userId = tostring(Players.LocalPlayer.UserId)
|
||||
local friendsRetrievalStatus = state.Friends.retrievalStatus[userId]
|
||||
if friendsRetrievalStatus ~= RetrievalStatus.Fetching then
|
||||
local networkImpl = httpRequest(HttpRbxApiService)
|
||||
store:dispatch(ApiFetchUsersFriends(networkImpl, userId, Constants.ThumbnailRequest.InviteToGame))
|
||||
end
|
||||
end
|
||||
}
|
||||
end)(ShareGamePageFrame)
|
||||
|
||||
return ShareGamePageFrame
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local User = require(AppTempCommon.LuaApp.Models.User)
|
||||
local ThumbnailRequest = require(AppTempCommon.LuaApp.Models.ThumbnailRequest)
|
||||
|
||||
local FStringSettingsHubInviteToGameThumbnailType = settings():GetFVariable("SettingsHubInviteToGameThumbnailType")
|
||||
local FStringSettingsHubInviteToGameThumbnailSize = settings():GetFVariable("SettingsHubInviteToGameThumbnailSize")
|
||||
|
||||
local DeviceLayout = {
|
||||
PHONE_PORTRAIT = "PHONE_PORTRAIT",
|
||||
PHONE_LANDSCAPE = "PHONE_LANDSCAPE",
|
||||
TABLET_PORTRAIT = "TABLET_PORTRAIT",
|
||||
TABLET_LANDSCAPE = "TABLET_LANDSCAPE",
|
||||
DESKTOP = "DESKTOP",
|
||||
}
|
||||
|
||||
local Color = {
|
||||
WHITE = Color3.fromRGB(255, 255, 255),
|
||||
GRAY1 = Color3.fromRGB(25, 25, 25),
|
||||
GRAY2 = Color3.fromRGB(117, 117, 117),
|
||||
GRAY3 = Color3.fromRGB(184, 184, 184),
|
||||
GRAY4 = Color3.fromRGB(227, 227, 227),
|
||||
GRAY5 = Color3.fromRGB(242, 242, 242),
|
||||
GRAY6 = Color3.fromRGB(245, 245, 245),
|
||||
RED = Color3.fromRGB(254, 68, 72),
|
||||
}
|
||||
|
||||
local Constants = {
|
||||
Color = Color,
|
||||
PresenceColors = {
|
||||
[User.PresenceType.ONLINE] = Color3.fromRGB(0, 162, 255),
|
||||
[User.PresenceType.IN_GAME] = Color3.fromRGB(2, 183, 87),
|
||||
[User.PresenceType.IN_STUDIO] = Color3.fromRGB(246, 136, 2),
|
||||
[User.PresenceType.OFFLINE] = Color.GRAY3,
|
||||
},
|
||||
PresenceTextKey = {
|
||||
[User.PresenceType.ONLINE] = "Common.Presence.Label.Online",
|
||||
[User.PresenceType.IN_GAME] = "Common.Presence.Label.InGame",
|
||||
[User.PresenceType.IN_STUDIO] = "Common.Presence.Label.InStudio",
|
||||
[User.PresenceType.OFFLINE] = "Common.Presence.Label.Offline",
|
||||
},
|
||||
InviteStatus = {
|
||||
Success = "Success", -- Should match API resultType
|
||||
Moderated = "Moderated", -- Should match API resultType
|
||||
Failed = "Failed",
|
||||
Pending = "Pending",
|
||||
},
|
||||
DeviceOrientation = {
|
||||
PORTRAIT = "PORTRAIT",
|
||||
LANDSCAPE = "LANDSCAPE",
|
||||
INVALID = "INVALID",
|
||||
},
|
||||
PageRoute = {
|
||||
NONE = "NONE",
|
||||
SETTINGS_HUB = "SETTINGS_HUB",
|
||||
SHARE_GAME = "SHARE_GAME",
|
||||
},
|
||||
|
||||
AvatarThumbnailTypes = {
|
||||
AvatarThumbnail = "AvatarThumbnail",
|
||||
HeadShot = "HeadShot",
|
||||
},
|
||||
AvatarThumbnailSizes = {
|
||||
Size48x48 = "Size48x48",
|
||||
Size60x60 = "Size60x60",
|
||||
Size100x100 = "Size100x100",
|
||||
Size150x150 = "Size150x150",
|
||||
Size352x352 = "Size352x352",
|
||||
},
|
||||
|
||||
SHARE_GAME_Z_INDEX = 2,
|
||||
|
||||
--[[
|
||||
Used for determining how the ShareGame page will be rendered across
|
||||
devices.
|
||||
]]
|
||||
DeviceLayout = DeviceLayout,
|
||||
LayoutSpecific = {
|
||||
[DeviceLayout.PHONE_LANDSCAPE] = {
|
||||
HEADER_HEIGHT = 40,
|
||||
PAGE_TITLE_TEXT_SIZE = 23,
|
||||
SEARCH_BOX_MARGIN = 12,
|
||||
PAGE_SIDE_MARGINS = 7,
|
||||
BACK_BUTTON_HEIGHT = 44,
|
||||
BACK_BUTTON_WIDTH = 44,
|
||||
EXTEND_BOTTOM_SIZE = 0,
|
||||
TOAST_HEIGHT = 40,
|
||||
},
|
||||
[DeviceLayout.PHONE_PORTRAIT] = {
|
||||
HEADER_HEIGHT = 40,
|
||||
PAGE_TITLE_TEXT_SIZE = 23,
|
||||
SEARCH_BOX_MARGIN = 15,
|
||||
PAGE_SIDE_MARGINS = 5,
|
||||
BACK_BUTTON_HEIGHT = 44,
|
||||
BACK_BUTTON_WIDTH = 44,
|
||||
EXTEND_BOTTOM_SIZE = 0,
|
||||
TOAST_HEIGHT = 40,
|
||||
},
|
||||
[DeviceLayout.TABLET_PORTRAIT] = {
|
||||
HEADER_HEIGHT = 40,
|
||||
PAGE_TITLE_TEXT_SIZE = 23,
|
||||
SEARCH_BOX_MARGIN = 15,
|
||||
PAGE_SIDE_MARGINS = 15,
|
||||
BACK_BUTTON_HEIGHT = 44,
|
||||
BACK_BUTTON_WIDTH = 44,
|
||||
EXTEND_BOTTOM_SIZE = 0,
|
||||
TOAST_HEIGHT = 80,
|
||||
},
|
||||
[DeviceLayout.TABLET_LANDSCAPE] = {
|
||||
HEADER_HEIGHT = 60,
|
||||
PAGE_TITLE_TEXT_SIZE = 23,
|
||||
SEARCH_BOX_MARGIN = 15,
|
||||
PAGE_SIDE_MARGINS = 5,
|
||||
BACK_BUTTON_HEIGHT = 44,
|
||||
BACK_BUTTON_WIDTH = 44,
|
||||
EXTEND_BOTTOM_SIZE = 68,
|
||||
TOAST_HEIGHT = 80,
|
||||
},
|
||||
[DeviceLayout.DESKTOP] = {
|
||||
HEADER_HEIGHT = 60,
|
||||
PAGE_TITLE_TEXT_SIZE = 29,
|
||||
SEARCH_BOX_MARGIN = 0,
|
||||
PAGE_SIDE_MARGINS = 0,
|
||||
BACK_BUTTON_HEIGHT = 44,
|
||||
BACK_BUTTON_WIDTH = 154,
|
||||
EXTEND_BOTTOM_SIZE = 68,
|
||||
TOAST_HEIGHT = 80,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Constants.InviteAvatarThumbnailType = Constants.AvatarThumbnailTypes[FStringSettingsHubInviteToGameThumbnailType]
|
||||
or Constants.AvatarThumbnailTypes.HeadShot
|
||||
|
||||
Constants.InviteAvatarThumbnailSize = Constants.AvatarThumbnailSizes[FStringSettingsHubInviteToGameThumbnailSize]
|
||||
or Constants.AvatarThumbnailSizes.Size100x100
|
||||
|
||||
Constants.ThumbnailRequest = {
|
||||
InviteToGame = {ThumbnailRequest.fromData(
|
||||
Constants.InviteAvatarThumbnailType,
|
||||
Constants.InviteAvatarThumbnailSize
|
||||
)},
|
||||
}
|
||||
|
||||
return Constants
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local FlagSettings = {}
|
||||
|
||||
local FFlagSettingsHubInviteToGameWindows = settings():GetFVariable("SettingsHubInviteToGameWindows5")
|
||||
local FFlagSettingsHubInviteToGameOSX = settings():GetFVariable("SettingsHubInviteToGameOSX5")
|
||||
local FFlagSettingsHubInviteToGameIOS = settings():GetFVariable("SettingsHubInviteToGameIOS5")
|
||||
local FFlagSettingsHubInviteToGameAndroid = settings():GetFVariable("SettingsHubInviteToGameAndroid5")
|
||||
local FFlagSettingsHubInviteToGameUWP = settings():GetFVariable("SettingsHubInviteToGameUWP5")
|
||||
|
||||
-- Helper function to throttle based on player Id:
|
||||
function FlagSettings.ThrottleUserId(throttle, userId)
|
||||
assert(type(throttle) == "number")
|
||||
assert(type(userId) == "number")
|
||||
|
||||
-- Determine userRollout using last two digits of user ID:
|
||||
-- (+1 to change range from 0-99 to 1-100 as 0 is off, 100 is full on):
|
||||
local userRollout = (userId % 100) + 1
|
||||
return userRollout <= throttle
|
||||
end
|
||||
|
||||
function FlagSettings.IsShareGamePageEnabledByPlatform(platform)
|
||||
local throttle
|
||||
if platform == Enum.Platform.Windows then
|
||||
throttle = FFlagSettingsHubInviteToGameWindows
|
||||
elseif platform == Enum.Platform.OSX then
|
||||
throttle = FFlagSettingsHubInviteToGameOSX
|
||||
elseif platform == Enum.Platform.IOS then
|
||||
throttle = FFlagSettingsHubInviteToGameIOS
|
||||
elseif platform == Enum.Platform.Android then
|
||||
throttle = FFlagSettingsHubInviteToGameAndroid
|
||||
elseif platform == Enum.Platform.UWP then
|
||||
throttle = FFlagSettingsHubInviteToGameUWP
|
||||
end
|
||||
|
||||
local throttleNumber = tonumber(throttle)
|
||||
if not throttleNumber then
|
||||
return false
|
||||
end
|
||||
|
||||
local userId = Players.LocalPlayer.UserId
|
||||
return FlagSettings.ThrottleUserId(throttleNumber, userId)
|
||||
end
|
||||
|
||||
return FlagSettings
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local Modules = CoreGui.RobloxGui.Modules
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Immutable = require(AppTempCommon.Common.Immutable)
|
||||
|
||||
local ClosePage = require(ShareGame.Actions.ClosePage)
|
||||
local SetSearchAreaActive = require(ShareGame.Actions.SetSearchAreaActive)
|
||||
local SetSearchText = require(ShareGame.Actions.SetSearchText)
|
||||
|
||||
local DEFAULT_STATE = {
|
||||
SearchAreaActive = false,
|
||||
SearchText = "",
|
||||
}
|
||||
|
||||
return function(state, action)
|
||||
state = state or DEFAULT_STATE
|
||||
|
||||
if action.type == SetSearchAreaActive.name then
|
||||
state = Immutable.Set(state, "SearchAreaActive", action.isActive)
|
||||
elseif action.type == SetSearchText.name then
|
||||
state = Immutable.Set(state, "SearchText", action.searchText)
|
||||
elseif action.type == ClosePage.name then
|
||||
state = DEFAULT_STATE
|
||||
end
|
||||
|
||||
return state
|
||||
end
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Immutable = require(AppTempCommon.Common.Immutable)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local SetDeviceLayout = require(ShareGame.Actions.SetDeviceLayout)
|
||||
local SetDeviceOrientation = require(AppTempCommon.LuaApp.Actions.SetDeviceOrientation)
|
||||
local SetIsSmallTouchScreen = require(ShareGame.Actions.SetIsSmallTouchScreen)
|
||||
|
||||
return function(state, action)
|
||||
state = state or {
|
||||
DeviceLayout = Constants.DeviceLayout.DESKTOP,
|
||||
DeviceOrientation = Constants.DeviceOrientation.INVALID,
|
||||
IsSmallTouchScreen = false,
|
||||
}
|
||||
|
||||
if action.type == SetDeviceOrientation.name then
|
||||
state = Immutable.Set(state, "DeviceOrientation", action.deviceOrientation)
|
||||
elseif action.type == SetDeviceLayout.name then
|
||||
state = Immutable.Set(state, "DeviceLayout", action.deviceLayout)
|
||||
elseif action.type == SetIsSmallTouchScreen.name then
|
||||
state = Immutable.Set(state, "IsSmallTouchScreen", action.isSmallTouchScreen)
|
||||
end
|
||||
|
||||
return state
|
||||
end
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Immutable = require(AppTempCommon.Common.Immutable)
|
||||
local ReceivedUserInviteStatus = require(ShareGame.Actions.ReceivedUserInviteStatus)
|
||||
|
||||
return function(state, action)
|
||||
state = state or {}
|
||||
|
||||
if action.type == ReceivedUserInviteStatus.name then
|
||||
state = Immutable.Set(state, action.userId, action.inviteStatus)
|
||||
end
|
||||
|
||||
return state
|
||||
end
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Immutable = require(AppTempCommon.Common.Immutable)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local OpenPage = require(ShareGame.Actions.OpenPage)
|
||||
local ClosePage = require(ShareGame.Actions.ClosePage)
|
||||
|
||||
return function(state, action)
|
||||
state = state or {
|
||||
IsOpen = false,
|
||||
Route = Constants.PageRoute.NONE,
|
||||
}
|
||||
|
||||
if action.type == OpenPage.name then
|
||||
state = Immutable.JoinDictionaries(state, {
|
||||
IsOpen = true,
|
||||
Route = action.route or Constants.PageRoute.NONE,
|
||||
})
|
||||
elseif action.type == ClosePage.name then
|
||||
state = Immutable.JoinDictionaries(state, {
|
||||
IsOpen = false,
|
||||
Route = action.route or Constants.PageRoute.NONE,
|
||||
})
|
||||
end
|
||||
|
||||
return state
|
||||
end
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
|
||||
local Immutable = require(AppTempCommon.Common.Immutable)
|
||||
local ReceivedUserInviteStatus = require(ShareGame.Actions.ReceivedUserInviteStatus)
|
||||
local StoppedToastTimer = require(ShareGame.Actions.StoppedToastTimer)
|
||||
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
return function(state, action)
|
||||
state = state or {
|
||||
failedInvites = {},
|
||||
}
|
||||
|
||||
if action.type == ReceivedUserInviteStatus.name then
|
||||
local inviteStatus = action.inviteStatus
|
||||
|
||||
if inviteStatus == InviteStatus.Moderated
|
||||
or inviteStatus == InviteStatus.Failed then
|
||||
local inviteStatusModel = {
|
||||
timeStamp = tick(),
|
||||
userId = action.userId,
|
||||
status = inviteStatus,
|
||||
}
|
||||
|
||||
state = Immutable.JoinDictionaries(state, {
|
||||
failedInvites = Immutable.JoinDictionaries(
|
||||
state.failedInvites, Immutable.Append(state.failedInvites, inviteStatusModel)
|
||||
),
|
||||
})
|
||||
end
|
||||
|
||||
elseif action.type == StoppedToastTimer.name then
|
||||
state = Immutable.JoinDictionaries(state, {
|
||||
failedInvites = {},
|
||||
})
|
||||
end
|
||||
|
||||
return state
|
||||
end
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
local IMAGE_PATH = "rbxasset://textures/ui/Settings/ShareGame/icons.png"
|
||||
|
||||
local function createFrameModel(offset, size)
|
||||
return {
|
||||
offset = offset,
|
||||
size = size,
|
||||
}
|
||||
end
|
||||
|
||||
local SHEET_MODEL = {
|
||||
frames = {
|
||||
back = createFrameModel(Vector2.new(2, 30), Vector2.new(24, 24)),
|
||||
clear = createFrameModel(Vector2.new(6, 62), Vector2.new(16, 16)),
|
||||
invite = createFrameModel(Vector2.new(2, 86), Vector2.new(24, 24)),
|
||||
search_border = createFrameModel(Vector2.new(11, 11), Vector2.new(7, 7)),
|
||||
search_large = createFrameModel(Vector2.new(3, 143), Vector2.new(22, 22)),
|
||||
search_small = createFrameModel(Vector2.new(6, 117), Vector2.new(16, 16)),
|
||||
friends = createFrameModel(Vector2.new(0, 170), Vector2.new(72, 72)),
|
||||
},
|
||||
}
|
||||
|
||||
local ShareGameIcons = {}
|
||||
|
||||
function ShareGameIcons:GetFrame(key)
|
||||
return SHEET_MODEL.frames[key]
|
||||
end
|
||||
|
||||
function ShareGameIcons:GetImagePath()
|
||||
return IMAGE_PATH
|
||||
end
|
||||
|
||||
function ShareGameIcons:ApplyImage(guiObject, key)
|
||||
local frameModel = self:GetFrame(key)
|
||||
|
||||
guiObject.Image = IMAGE_PATH
|
||||
guiObject.ImageRectOffset = frameModel.offset
|
||||
guiObject.ImageRectSize = frameModel.size
|
||||
end
|
||||
|
||||
return ShareGameIcons
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local AppTempCommon = CorePackages.AppTempCommon
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
|
||||
local ApiSendGameInvite = require(AppTempCommon.LuaApp.Thunks.ApiSendGameInvite)
|
||||
local ApiFetchPlaceInfos = require(AppTempCommon.LuaApp.Thunks.ApiFetchPlaceInfos)
|
||||
local Promise = require(AppTempCommon.LuaApp.Promise)
|
||||
|
||||
local ReceivedUserInviteStatus = require(ShareGame.Actions.ReceivedUserInviteStatus)
|
||||
local Constants = require(ShareGame.Constants)
|
||||
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
return function(requestImpl, userId, placeId)
|
||||
return function(store)
|
||||
local latestState = store:getState()
|
||||
|
||||
return Promise.new(function(resolve, reject)
|
||||
-- Check that we haven't already invited this user
|
||||
if latestState.Invites[tostring(userId)] == InviteStatus.Pending then
|
||||
reject()
|
||||
else
|
||||
resolve()
|
||||
end
|
||||
end):andThen(function()
|
||||
local maybePlaceInfo = latestState.PlaceInfos[placeId]
|
||||
|
||||
return Promise.new(function(resolve, reject)
|
||||
-- Log that we've tried inviting this user
|
||||
store:dispatch(ReceivedUserInviteStatus(userId, InviteStatus.Pending))
|
||||
|
||||
if maybePlaceInfo then
|
||||
resolve(maybePlaceInfo)
|
||||
else
|
||||
store:dispatch(ApiFetchPlaceInfos(requestImpl, {placeId})):andThen(function(placeInfos)
|
||||
if placeInfos[1] ~= nil then
|
||||
resolve(placeInfos[1])
|
||||
else
|
||||
reject()
|
||||
end
|
||||
end, function()
|
||||
reject()
|
||||
end)
|
||||
end
|
||||
end):andThen(function(placeInfo)
|
||||
return store:dispatch(ApiSendGameInvite(requestImpl, userId, placeInfo))
|
||||
end):andThen(function(results)
|
||||
store:dispatch(ReceivedUserInviteStatus(userId, results.resultType))
|
||||
return results
|
||||
end, function()
|
||||
store:dispatch(ReceivedUserInviteStatus(userId, InviteStatus.Failed))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
--[[
|
||||
Placeholder page for SettingsHub that manages top level Roact view
|
||||
|
||||
This page is a container and controller for the Roact app
|
||||
--]]
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
|
||||
local ShareGame = RobloxGui.Modules.Settings.Pages.ShareGame
|
||||
local Constants = require(ShareGame.Constants)
|
||||
local OpenPage = require(ShareGame.Actions.OpenPage)
|
||||
local ClosePage = require(ShareGame.Actions.ClosePage)
|
||||
|
||||
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
|
||||
local this = settingsPageFactory:CreateNewPage()
|
||||
|
||||
this.TabHeader = nil -- no tab for this page
|
||||
this.PageListLayout.Parent = nil -- no list layout for this page
|
||||
this.ShouldShowBottomBar = false
|
||||
this.ShouldShowHubBar = false
|
||||
this.IsPageClipped = false
|
||||
|
||||
this.Page.Name = "ShareGameDummy"
|
||||
this.Page.Size = UDim2.new(1, 0, 0, 0)
|
||||
|
||||
|
||||
function this:ConnectHubToApp(settingsHub, shareGameApp)
|
||||
this:SetHub(settingsHub)
|
||||
|
||||
shareGameApp.store.changed:connect(function(state, prevState)
|
||||
|
||||
local page = state.Page
|
||||
local wasOpen = prevState.Page.IsOpen
|
||||
|
||||
-- Check if the user closed the page via the Roact app.
|
||||
if page.Route == Constants.PageRoute.SHARE_GAME and (wasOpen and not page.IsOpen) then
|
||||
-- Close the page to sync up Settings Hub with the state change
|
||||
this.HubRef:PopMenu(nil, true)
|
||||
end
|
||||
end)
|
||||
|
||||
this.Displayed.Event:Connect(function()
|
||||
local state = shareGameApp.store:getState()
|
||||
if not state.Page.IsOpen then
|
||||
-- Tell Roact app that the page was opened via Settings Hub
|
||||
shareGameApp.store:dispatch(OpenPage(Constants.PageRoute.SETTINGS_HUB))
|
||||
end
|
||||
end)
|
||||
|
||||
this.Hidden.Event:Connect(function()
|
||||
-- The user closed the page via the Settings Hub (instead of
|
||||
-- pressing back on the page), so we have to sync the app state up
|
||||
-- with the Settings Hub action.
|
||||
local state = shareGameApp.store:getState()
|
||||
if state.Page.IsOpen then
|
||||
shareGameApp.store:dispatch(ClosePage(Constants.PageRoute.SETTINGS_HUB))
|
||||
end
|
||||
end)
|
||||
|
||||
shareGameApp.store:dispatch(ClosePage(Constants.PageRoute.SETTINGS_HUB))
|
||||
end
|
||||
|
||||
return this
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Settings = script.Parent.Parent
|
||||
local Actions = Settings.Actions
|
||||
--TODO: Currently Under Migration to CorePackages
|
||||
local Immutable = require(CorePackages.AppTempCommon.Common.Immutable)
|
||||
--
|
||||
local SetRecentlyPlayedGames = require(Actions.SetRecentlyPlayedGames)
|
||||
local Game = require(Settings.Models.Game)
|
||||
|
||||
return function(state, action)
|
||||
state = state or {}
|
||||
if action.type == SetRecentlyPlayedGames.name then
|
||||
local gameSortState = state.gameSort or {}
|
||||
local gameSortData = action.gameSortData
|
||||
local gameSort = {}
|
||||
if gameSortData then
|
||||
for i in ipairs(gameSortData) do
|
||||
local gameData = Game.fromGameCache(gameSortData[i])
|
||||
table.insert(gameSort, gameData)
|
||||
end
|
||||
end
|
||||
state = {
|
||||
gameSort = Immutable.JoinDictionaries(gameSortState, gameSort),
|
||||
}
|
||||
end
|
||||
return state
|
||||
end
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Settings = script.Parent.Parent
|
||||
local Actions = Settings.Actions
|
||||
--TODO: Currently Under Migration to CorePackages
|
||||
local Immutable = require(CorePackages.AppTempCommon.Common.Immutable)
|
||||
--
|
||||
local SetRecentlyPlayedGamesFetching = require(Actions.SetRecentlyPlayedGamesFetching)
|
||||
|
||||
return function(state, action)
|
||||
state = state or {}
|
||||
if action.type == SetRecentlyPlayedGamesFetching.name then
|
||||
state = {
|
||||
fetching = action.fetching,
|
||||
}
|
||||
end
|
||||
return state
|
||||
end
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
local Reducers = script.Parent
|
||||
local RecentlyPlayedGamesFetchingStatus = require(Reducers.RecentlyPlayedGamesFetchingStatus)
|
||||
local RecentlyPlayedGames = require(Reducers.RecentlyPlayedGames)
|
||||
|
||||
return function(state, action)
|
||||
state = state or {}
|
||||
|
||||
return {
|
||||
RecentlyPlayedGamesFetchingStatus = RecentlyPlayedGamesFetchingStatus(state.RecentlyPlayedGamesFetchingStatus, action),
|
||||
RecentlyPlayedGames = RecentlyPlayedGames(state.RecentlyPlayedGames, action),
|
||||
}
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,370 @@
|
||||
--[[
|
||||
Filename: SettingsPageFactory.lua
|
||||
Written by: jeditkacheff
|
||||
Version 1.0
|
||||
Description: Base Page Functionality for all Settings Pages
|
||||
--]]
|
||||
----------------- SERVICES ------------------------------
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local HttpService = game:GetService("HttpService")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
|
||||
----------- UTILITIES --------------
|
||||
local utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
local StyleWidgets = require(RobloxGui.Modules.StyleWidgets)
|
||||
|
||||
|
||||
----------- VARIABLES --------------
|
||||
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
|
||||
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
|
||||
|
||||
local success, result = pcall(function() return settings():GetFFlag('UseNotificationsLocalization') end)
|
||||
local FFlagUseNotificationsLocalization = success and result
|
||||
|
||||
----------- CONSTANTS --------------
|
||||
local HEADER_SPACING = 5
|
||||
if utility:IsSmallTouchScreen() then
|
||||
HEADER_SPACING = 0
|
||||
end
|
||||
|
||||
----------- CLASS DECLARATION --------------
|
||||
local function Initialize()
|
||||
local this = {}
|
||||
this.HubRef = nil
|
||||
this.LastSelectedObject = nil
|
||||
this.TabPosition = 0
|
||||
this.Active = false
|
||||
this.OpenStateChangedCount = 0
|
||||
this.ShouldShowBottomBar = true
|
||||
this.ShouldShowHubBar = true
|
||||
this.IsPageClipped = true
|
||||
local rows = {}
|
||||
local displayed = false
|
||||
|
||||
------ TAB CREATION -------
|
||||
this.TabHeader = utility:Create'TextButton'
|
||||
{
|
||||
Name = "Header",
|
||||
Text = "",
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1/5, 0,1,0),
|
||||
Position = UDim2.new(0,0,0,0)
|
||||
};
|
||||
if utility:IsSmallTouchScreen() then
|
||||
this.TabHeader.Size = UDim2.new(0,84,1,0)
|
||||
elseif isTenFootInterface then
|
||||
this.TabHeader.Size = UDim2.new(0,220,1,0)
|
||||
end
|
||||
this.TabHeader.MouseButton1Click:connect(function()
|
||||
if this.HubRef then
|
||||
this.HubRef:SwitchToPage(this, true)
|
||||
end
|
||||
end)
|
||||
|
||||
local icon = utility:Create'ImageLabel'
|
||||
{
|
||||
Name = "Icon",
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0.75, 0, 0.75, 0),
|
||||
Position = UDim2.new(0,10,0.5,-18),
|
||||
Image = "",
|
||||
ImageTransparency = 0.5,
|
||||
Parent = this.TabHeader
|
||||
};
|
||||
local iconAspectRatio = utility:Create'UIAspectRatioConstraint'
|
||||
{
|
||||
Name = "AspectRatioConstraint",
|
||||
AspectRatio = 1,
|
||||
Parent = icon
|
||||
};
|
||||
|
||||
local title = utility:Create'TextLabel'
|
||||
{
|
||||
Name = "Title",
|
||||
Text = "",
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size24,
|
||||
TextColor3 = Color3.new(1,1,1),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1.05,0,1,0), --overwritten
|
||||
Position = UDim2.new(1.2,0,0,0), --overwritten
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextTransparency = 0.5
|
||||
};
|
||||
|
||||
local titleTextSizeConstraint = Instance.new("UITextSizeConstraint")
|
||||
titleTextSizeConstraint.MaxTextSize = 24
|
||||
if FFlagUseNotificationsLocalization then
|
||||
title.Parent = this.TabHeader
|
||||
title.TextScaled = true
|
||||
title.TextWrapped = true
|
||||
titleTextSizeConstraint.Parent = title
|
||||
else
|
||||
title.Parent = icon
|
||||
end
|
||||
|
||||
if utility:IsSmallTouchScreen() then
|
||||
title.FontSize = Enum.FontSize.Size18
|
||||
titleTextSizeConstraint.MaxTextSize = 18
|
||||
elseif isTenFootInterface then
|
||||
title.FontSize = Enum.FontSize.Size48
|
||||
titleTextSizeConstraint.MaxTextSize = 48
|
||||
end
|
||||
|
||||
local tabSelection = StyleWidgets.MakeTabSelectionWidget(this.TabHeader)
|
||||
local titleScaleInitial = Vector2.new(title.Size.X.Scale, title.Size.Y.Scale)
|
||||
local function onResized()
|
||||
if not this.TabHeader then
|
||||
return
|
||||
end
|
||||
|
||||
if utility:IsSmallTouchScreen() then
|
||||
this.TabHeader.Icon.Size = UDim2.new(0,34,0,28)
|
||||
this.TabHeader.Icon.Position = UDim2.new(this.TabHeader.Icon.Position.X.Scale,this.TabHeader.Icon.Position.X.Offset,0.5,-14)
|
||||
this.TabHeader.Icon.AnchorPoint = Vector2.new(0, 0)
|
||||
elseif isTenFootInterface then
|
||||
this.TabHeader.Icon.Size = UDim2.new(0,88,0,74)
|
||||
this.TabHeader.Icon.Position = UDim2.new(0,0,0.5,0)
|
||||
this.TabHeader.Icon.AnchorPoint = Vector2.new(0, 0.5)
|
||||
else
|
||||
this.TabHeader.Icon.Size = UDim2.new(0,44,0,37)
|
||||
this.TabHeader.Icon.Position = UDim2.new(0,15,0.5,-18)
|
||||
this.TabHeader.Icon.AnchorPoint = Vector2.new(0, 0)
|
||||
end
|
||||
|
||||
local isPortrait = utility:IsPortrait()
|
||||
if isPortrait then
|
||||
this.TabHeader.Icon.Position = UDim2.new(0.5, 0, 0.5, 0)
|
||||
this.TabHeader.Icon.AnchorPoint = Vector2.new(0.5, 0.5)
|
||||
this.TabHeader.Icon.Size = UDim2.new(0.5, 0, 0.5, 0)
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.Visible = false
|
||||
else
|
||||
this.TabHeader.Icon.Title.Visible = false
|
||||
end
|
||||
else
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.Visible = true
|
||||
else
|
||||
this.TabHeader.Icon.Title.Visible = true
|
||||
end
|
||||
end
|
||||
|
||||
if FFlagUseNotificationsLocalization then
|
||||
local iconSize = this.TabHeader.Icon.AbsoluteSize
|
||||
local paddingLeft = 0.125
|
||||
local paddingRight = 0.025
|
||||
|
||||
title.Position = UDim2.new(
|
||||
paddingLeft,
|
||||
iconSize.X,
|
||||
0.225,
|
||||
0
|
||||
)
|
||||
title.Size = UDim2.new(
|
||||
titleScaleInitial.X - paddingLeft - paddingRight,
|
||||
-iconSize.X,
|
||||
0.5,
|
||||
0
|
||||
)
|
||||
end
|
||||
end --end local function onResized()
|
||||
|
||||
utility:OnResized(this.TabHeader, onResized)
|
||||
|
||||
------ PAGE CREATION -------
|
||||
this.Page = utility:Create'Frame'
|
||||
{
|
||||
Name = "Page",
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1,0,1,0)
|
||||
};
|
||||
|
||||
this.PageListLayout = utility:Create'UIListLayout'
|
||||
{
|
||||
Name = "RowListLayout",
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
Padding = UDim.new(0, 3),
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Parent = this.Page
|
||||
};
|
||||
|
||||
|
||||
-- make sure each page has a unique selection group (for gamepad selection)
|
||||
GuiService:AddSelectionParent(HttpService:GenerateGUID(false), this.Page)
|
||||
|
||||
----------------- Events ------------------------
|
||||
|
||||
this.Displayed = Instance.new("BindableEvent")
|
||||
this.Displayed.Name = "Displayed"
|
||||
|
||||
this.Displayed.Event:connect(function()
|
||||
if not this.HubRef.Shield.Visible then return end
|
||||
|
||||
this:SelectARow()
|
||||
end)
|
||||
|
||||
this.Hidden = Instance.new("BindableEvent")
|
||||
this.Hidden.Event:connect(function()
|
||||
if GuiService.SelectedCoreObject and GuiService.SelectedCoreObject:IsDescendantOf(this.Page) then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end)
|
||||
this.Hidden.Name = "Hidden"
|
||||
|
||||
----------------- FUNCTIONS ------------------------
|
||||
function this:SelectARow(forced) -- Selects the first row or the most recently selected row
|
||||
if forced or not GuiService.SelectedCoreObject or not GuiService.SelectedCoreObject:IsDescendantOf(this.Page) then
|
||||
if this.LastSelectedObject then
|
||||
GuiService.SelectedCoreObject = this.LastSelectedObject
|
||||
else
|
||||
if rows and #rows > 0 then
|
||||
local valueChangerFrame = nil
|
||||
|
||||
if type(rows[1].ValueChanger) ~= "table" then
|
||||
valueChangerFrame = rows[1].ValueChanger
|
||||
else
|
||||
valueChangerFrame = rows[1].ValueChanger.SliderFrame and
|
||||
rows[1].ValueChanger.SliderFrame or rows[1].ValueChanger.SelectorFrame
|
||||
end
|
||||
GuiService.SelectedCoreObject = valueChangerFrame
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:Display(pageParent, skipAnimation)
|
||||
this.OpenStateChangedCount = this.OpenStateChangedCount + 1
|
||||
|
||||
if this.TabHeader then
|
||||
this.TabHeader.TabSelection.Visible = true
|
||||
this.TabHeader.Icon.ImageTransparency = 0
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.TextTransparency = 0
|
||||
else
|
||||
this.TabHeader.Icon.Title.TextTransparency = 0
|
||||
end
|
||||
end
|
||||
|
||||
this.Page.Parent = pageParent
|
||||
this.Page.Visible = true
|
||||
|
||||
local endPos = UDim2.new(0,0,0,0)
|
||||
local animationComplete = function()
|
||||
this.Page.Visible = true
|
||||
displayed = true
|
||||
this.Displayed:Fire()
|
||||
end
|
||||
if skipAnimation then
|
||||
this.Page.Position = endPos
|
||||
animationComplete()
|
||||
else
|
||||
this.Page:TweenPosition(endPos, Enum.EasingDirection.In, Enum.EasingStyle.Quad, 0.1, true, animationComplete)
|
||||
end
|
||||
end
|
||||
function this:Hide(direction, newPagePos, skipAnimation, delayBeforeHiding)
|
||||
this.OpenStateChangedCount = this.OpenStateChangedCount + 1
|
||||
|
||||
if this.TabHeader then
|
||||
this.TabHeader.TabSelection.Visible = false
|
||||
this.TabHeader.Icon.ImageTransparency = 0.5
|
||||
if FFlagUseNotificationsLocalization then
|
||||
this.TabHeader.Title.TextTransparency = 0.5
|
||||
else
|
||||
this.TabHeader.Icon.Title.TextTransparency = 0.5
|
||||
end
|
||||
end
|
||||
|
||||
if this.Page.Parent then
|
||||
local endPos = UDim2.new(1 * direction,0,0,0)
|
||||
local animationComplete = function()
|
||||
this.Page.Visible = false
|
||||
this.Page.Position = UDim2.new(this.TabPosition - newPagePos,0,0,0)
|
||||
displayed = false
|
||||
this.Hidden:Fire()
|
||||
end
|
||||
|
||||
local remove = function()
|
||||
if skipAnimation then
|
||||
this.Page.Position = endPos
|
||||
animationComplete()
|
||||
else
|
||||
this.Page:TweenPosition(endPos, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.1, true, animationComplete)
|
||||
end
|
||||
end
|
||||
|
||||
if delayBeforeHiding then
|
||||
local myOpenStateChangedCount = this.OpenStateChangedCount
|
||||
delay(delayBeforeHiding, function()
|
||||
if myOpenStateChangedCount == this.OpenStateChangedCount then
|
||||
remove()
|
||||
end
|
||||
end)
|
||||
else
|
||||
remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetDisplayed()
|
||||
return displayed
|
||||
end
|
||||
|
||||
function this:GetVisibility()
|
||||
return this.Page.Parent
|
||||
end
|
||||
|
||||
function this:GetTabHeader()
|
||||
return this.TabHeader
|
||||
end
|
||||
|
||||
function this:SetHub(hubRef)
|
||||
this.HubRef = hubRef
|
||||
|
||||
for i, row in next, rows do
|
||||
if type(row.ValueChanger) == 'table' then
|
||||
row.ValueChanger.HubRef = this.HubRef
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetSize()
|
||||
return this.Page.AbsoluteSize
|
||||
end
|
||||
|
||||
function this:AddRow(RowFrame, RowLabel, ValueChangerInstance, ExtraRowSpacing)
|
||||
rows[#rows + 1] = {SelectionFrame = RowFrame, Label = RowLabel, ValueChanger = ValueChangerInstance}
|
||||
|
||||
local rowFrameYSize = 0
|
||||
if RowFrame then
|
||||
rowFrameYSize = RowFrame.Size.Y.Offset
|
||||
end
|
||||
|
||||
if ExtraRowSpacing then
|
||||
this.Page.Size = UDim2.new(1, 0, 0, this.Page.Size.Y.Offset + rowFrameYSize + ExtraRowSpacing)
|
||||
else
|
||||
this.Page.Size = UDim2.new(1, 0, 0, this.Page.Size.Y.Offset + rowFrameYSize)
|
||||
end
|
||||
|
||||
if this.HubRef and type(ValueChangerInstance) == 'table' then
|
||||
ValueChangerInstance.HubRef = this.HubRef
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
-------- public facing API ----------------
|
||||
local moduleApiTable = {}
|
||||
|
||||
function moduleApiTable:CreateNewPage()
|
||||
return Initialize()
|
||||
end
|
||||
|
||||
return moduleApiTable
|
||||
@@ -0,0 +1,20 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Settings = script.Parent
|
||||
|
||||
local SettingsReducer = require(Settings.Reducers.SettingsReducer)
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local Store = Rodux.Store
|
||||
|
||||
local SettingsState = {}
|
||||
|
||||
function SettingsState:Init()
|
||||
self.store = Store.new(SettingsReducer, nil, {Rodux.thunkMiddleware})
|
||||
end
|
||||
|
||||
function SettingsState:Destruct()
|
||||
self.store:destruct()
|
||||
end
|
||||
|
||||
SettingsState:Init()
|
||||
|
||||
return SettingsState
|
||||
@@ -0,0 +1,30 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
|
||||
local ShareGame = Modules.Settings.Pages.ShareGame
|
||||
local App = require(ShareGame.Components.App)
|
||||
local AppReducer = require(ShareGame.AppReducer)
|
||||
|
||||
local ShareGameMaster = {}
|
||||
|
||||
function ShareGameMaster.createApp(parentGui)
|
||||
local self = {}
|
||||
self.store = Rodux.Store.new(AppReducer, nil, { Rodux.thunkMiddleware })
|
||||
|
||||
self._instanceHandle = Roact.mount(
|
||||
Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = self.store
|
||||
}, {
|
||||
Roact.createElement(App, {
|
||||
pageTarget = parentGui,
|
||||
})
|
||||
})
|
||||
)
|
||||
return self
|
||||
end
|
||||
|
||||
return ShareGameMaster
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
local Actions = script.Parent.Parent.Actions
|
||||
local SetRecentlyPlayedGamesFetching = require(Actions.SetRecentlyPlayedGamesFetching)
|
||||
local SetRecentlyPlayedGames = require(Actions.SetRecentlyPlayedGames)
|
||||
|
||||
local RETRIES = 6
|
||||
local RECENT_SORT_KEY = "-2"
|
||||
local FEATURED_SORT_KEY = "8"
|
||||
|
||||
local function fetchRecentlyPlayedGamesAsync(store, numOfGames)
|
||||
local success = false
|
||||
local tryCount = 1
|
||||
-- This retry logic is here for those cases where the appshell data model failed to get the recent sort and is retrying.
|
||||
while tryCount <= RETRIES do
|
||||
local sharedData = PlatformService:ReadSharedData() or {}
|
||||
local gameCache = sharedData.games
|
||||
if sharedData.sorts ~= nil and gameCache ~= nil then
|
||||
local gamesIncluded = {}
|
||||
local count = 0
|
||||
local recentSortData = {}
|
||||
if sharedData.sorts[RECENT_SORT_KEY] ~= nil and sharedData.sorts[RECENT_SORT_KEY].orderedSort ~= nil then
|
||||
local recentSort = sharedData.sorts[RECENT_SORT_KEY].orderedSort
|
||||
for i in ipairs(recentSort) do
|
||||
if recentSort[i].HasData == false or count == numOfGames then
|
||||
break
|
||||
end
|
||||
--Check if there are cached game data available.
|
||||
local gameData = sharedData.games[tostring(recentSort[i].PlaceId)]
|
||||
if gameData then
|
||||
gamesIncluded[recentSort[i].PlaceId] = true
|
||||
table.insert(recentSortData, gameData)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if count < numOfGames then
|
||||
-- Add recommended games to fill up the remining games.
|
||||
if sharedData.sorts[FEATURED_SORT_KEY] ~= nil and sharedData.sorts[FEATURED_SORT_KEY].orderedSort ~= nil then
|
||||
local backupSort = sharedData.sorts[FEATURED_SORT_KEY].orderedSort
|
||||
for i in ipairs(backupSort) do
|
||||
if backupSort[i].HasData == false or count == numOfGames then
|
||||
break
|
||||
end
|
||||
local gameData = sharedData.games[tostring(backupSort[i].PlaceId)]
|
||||
if gameData and gamesIncluded[backupSort[i].PlaceId] ~= true then
|
||||
gamesIncluded[backupSort[i].PlaceId] = true
|
||||
table.insert(recentSortData, gameData)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if count >= numOfGames then
|
||||
store:dispatch(SetRecentlyPlayedGames(recentSortData))
|
||||
store:dispatch(SetRecentlyPlayedGamesFetching(false))
|
||||
success = true
|
||||
break
|
||||
end
|
||||
end
|
||||
tryCount = tryCount + 1
|
||||
wait(tryCount ^ 2)
|
||||
end
|
||||
if success == false then
|
||||
store:dispatch(SetRecentlyPlayedGames(nil))
|
||||
end
|
||||
end
|
||||
|
||||
return function(numOfGames, forceUpdate)
|
||||
return function(store)
|
||||
local state = store:getState()
|
||||
local isFetching = state.RecentlyPlayedGamesFetchingStatus.fetching
|
||||
if isFetching then
|
||||
return
|
||||
elseif isFetching ~= nil and not forceUpdate then
|
||||
return
|
||||
end
|
||||
store:dispatch(SetRecentlyPlayedGamesFetching(true))
|
||||
spawn(function()
|
||||
fetchRecentlyPlayedGamesAsync(store, numOfGames)
|
||||
end)
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user