add gs
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"lint": {
|
||||
"SameLineStatement": "fatal",
|
||||
"LocalUnused": "fatal",
|
||||
"FunctionUnused": "fatal",
|
||||
"ImportUnused": "fatal",
|
||||
"LocalShadow": "fatal",
|
||||
"ImplicitReturn": "fatal"
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("CANCEL_LEAVING_GAME", function()
|
||||
return {}
|
||||
end)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("CLOSE_NATIVE_PROMPT", function()
|
||||
return {}
|
||||
end)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("CLOSE_REPORT_DIALOG", function()
|
||||
return {}
|
||||
end)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("CLOSE_REPORT_SENT_DIALOG", function()
|
||||
return {}
|
||||
end)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("OPEN_NATIVE_CLOSE_PROMPT", function()
|
||||
return {}
|
||||
end)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("OPEN_REPORT_DIALOG", function(userId, userName)
|
||||
return {
|
||||
userId = userId,
|
||||
userName = userName,
|
||||
}
|
||||
end)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("OPEN_REPORT_SENT_DIALOG", function()
|
||||
return {}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local Modules = game:GetService("CorePackages").AppTempCommon
|
||||
local Action = require(Modules.Common.Action)
|
||||
|
||||
return Action("RECEIVED_USER_INVITE_STATUS", function(userId, inviteStatus)
|
||||
return {
|
||||
userId = tostring(userId),
|
||||
inviteStatus = inviteStatus,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_CONTROL_LAYOUT", function(controlLayout)
|
||||
return {
|
||||
controlLayout = controlLayout
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_CURRENT_PAGE", function(pageKey)
|
||||
return {
|
||||
newPage = pageKey,
|
||||
}
|
||||
end)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--Remove with isNewGamepadMenuEnabled
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_GAME_DESCRIPTIONS", function(newDescriptions)
|
||||
return {
|
||||
newDescriptions = newDescriptions,
|
||||
}
|
||||
end)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_FRIEND_STATUS", function(userId, friendStatus)
|
||||
return {
|
||||
userId = userId,
|
||||
friendStatus = friendStatus,
|
||||
}
|
||||
end)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_GAME_NAME_AND_DESCRIPTIONS", function(gameNameLocaleMap, gameDescriptionsLocaleMap, sourceLocale)
|
||||
return {
|
||||
gameNames = gameNameLocaleMap,
|
||||
gameDescriptions = gameDescriptionsLocaleMap,
|
||||
sourceLocale = sourceLocale,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_INPUT_TYPE", function(inputType)
|
||||
return {
|
||||
inputType = inputType,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_INSPECT_MENU_ENABLED", function(enabled)
|
||||
return {
|
||||
enabled = enabled,
|
||||
}
|
||||
end)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--Remove with isNewGamepadMenuEnabled
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_LANGUAGE_CODE_MAP", function(languageCodeMap)
|
||||
return {
|
||||
languageCodeMap = languageCodeMap,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_LOCALE_ID", function(localeId)
|
||||
return {
|
||||
newLocaleId = localeId,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_MENU_ICON_TOOLTIP_OPEN", function(status)
|
||||
return {
|
||||
menuIconTooltipOpen = status,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_MENU_OPEN", function(isMenuOpen)
|
||||
return {
|
||||
isMenuOpen = isMenuOpen,
|
||||
}
|
||||
end)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--Remove with isNewGamepadMenuEnabled
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_GAME_NAMES", function(newNames)
|
||||
return {
|
||||
newNames = newNames,
|
||||
}
|
||||
end)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_PLACE_INFO", function(placeName, placeDescription)
|
||||
return {
|
||||
placeName = placeName,
|
||||
placeDescription = placeDescription,
|
||||
}
|
||||
end)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_RESPAWN_BEHAVIOR", function(respawnEnabled, respawnCallback)
|
||||
return {
|
||||
respawnEnabled = respawnEnabled,
|
||||
customCallback = respawnCallback,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_RESPAWNING", function(isRespawning)
|
||||
return {
|
||||
respawning = isRespawning,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_SCREEN_SIZE", function(screenSize)
|
||||
return {
|
||||
newScreenSize = screenSize,
|
||||
}
|
||||
end)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--Remove with isNewGamepadMenuEnabled
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_SOURCE_LANGUAGE_CODE", function(languageCode)
|
||||
return {
|
||||
languageCode = languageCode,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("SET_VIDEO_RECORDING", function (recording)
|
||||
return {
|
||||
recording = recording,
|
||||
}
|
||||
end)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action("START_LEAVING_GAME", function()
|
||||
return {}
|
||||
end)
|
||||
@@ -0,0 +1,90 @@
|
||||
--[[
|
||||
The topbar button that opens the in-game menu.
|
||||
]]
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
|
||||
local InGameMenuPolicy = require(RobloxGui.Modules.InGameMenu.InGameMenuPolicy)
|
||||
|
||||
local ViewportOverlay = require(script.Parent.ViewportOverlay)
|
||||
local SideNavigation = require(script.Parent.SideNavigation)
|
||||
local LeaveGameDialog = require(script.Parent.LeaveGameDialog)
|
||||
local PageContainer = require(script.Parent.PageContainer)
|
||||
local RespawnDialog = require(script.Parent.RespawnDialog)
|
||||
local ReportDialog = require(script.Parent.ReportPage.ReportDialog)
|
||||
local ReportSentDialog = require(script.Parent.ReportPage.ReportSentDialog)
|
||||
local ControlLayoutSetter = require(script.Parent.ControlsPage.ControlLayoutSetter)
|
||||
local Connection = require(script.Parent.Connection)
|
||||
local EducationalPopup = require(script.Parent.EducationalPopup)
|
||||
local MenuIconTooltip = require(script.Parent.MenuIconTooltip)
|
||||
local FullscreenTitleBar = require(script.Parent.FullscreenTitleBar)
|
||||
|
||||
local GetFFlagEducationalPopupOnNativeClose = require(RobloxGui.Modules.Flags.GetFFlagEducationalPopupOnNativeClose)
|
||||
local FFlagLuaMenuPerfImprovements = require(script.Parent.Parent.Flags.FFlagLuaMenuPerfImprovements)
|
||||
local GetFFlagInGameMenuIconTooltip = require(RobloxGui.Modules.Flags.GetFFlagInGameMenuIconTooltip)
|
||||
local GetFFlagUseRoactPolicyProvider = require(RobloxGui.Modules.Flags.GetFFlagUseRoactPolicyProvider)
|
||||
|
||||
local Constants = require(script.Parent.Parent.Resources.Constants)
|
||||
|
||||
local function App(props)
|
||||
local fullscreenTitleBar = nil
|
||||
if props.enableFullscreenTitleBar then
|
||||
fullscreenTitleBar = Roact.createElement(FullscreenTitleBar, {
|
||||
displayOrder = Constants.DisplayOrder.FullscreenTitleBar,
|
||||
})
|
||||
end
|
||||
|
||||
if FFlagLuaMenuPerfImprovements then
|
||||
return Roact.createFragment({
|
||||
Content = props.visible and Roact.createFragment({
|
||||
Overlay = Roact.createElement(ViewportOverlay),
|
||||
SideNavigation = Roact.createElement(SideNavigation),
|
||||
LeaveGameDialog = Roact.createElement(LeaveGameDialog),
|
||||
PageContainer = Roact.createElement(PageContainer),
|
||||
RespawnDialog = Roact.createElement(RespawnDialog),
|
||||
ReportDialog = Roact.createElement(ReportDialog),
|
||||
ReportSentDialog = Roact.createElement(ReportSentDialog),
|
||||
ControlLayoutSetter = Roact.createElement(ControlLayoutSetter),
|
||||
}) or nil,
|
||||
Connection = Roact.createElement(Connection),
|
||||
EducationalPopup = GetFFlagEducationalPopupOnNativeClose() and Roact.createElement(EducationalPopup) or nil,
|
||||
MenuIconTooltip = GetFFlagInGameMenuIconTooltip() and Roact.createElement(MenuIconTooltip) or nil,
|
||||
FullscreenTitleBar = fullscreenTitleBar,
|
||||
})
|
||||
else
|
||||
return Roact.createFragment({
|
||||
Overlay = Roact.createElement(ViewportOverlay),
|
||||
SideNavigation = Roact.createElement(SideNavigation),
|
||||
LeaveGameDialog = Roact.createElement(LeaveGameDialog),
|
||||
PageContainer = Roact.createElement(PageContainer),
|
||||
RespawnDialog = Roact.createElement(RespawnDialog),
|
||||
ReportDialog = Roact.createElement(ReportDialog),
|
||||
ReportSentDialog = Roact.createElement(ReportSentDialog),
|
||||
ControlLayoutSetter = Roact.createElement(ControlLayoutSetter),
|
||||
Connection = Roact.createElement(Connection),
|
||||
EducationalPopup = GetFFlagEducationalPopupOnNativeClose() and Roact.createElement(EducationalPopup) or nil,
|
||||
MenuIconTooltip = GetFFlagInGameMenuIconTooltip() and Roact.createElement(MenuIconTooltip) or nil,
|
||||
FullscreenTitleBar = fullscreenTitleBar,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if GetFFlagUseRoactPolicyProvider() then
|
||||
App = InGameMenuPolicy.connect(function(appPolicy, props)
|
||||
return {
|
||||
enableFullscreenTitleBar = appPolicy.enableFullscreenTitleBar(),
|
||||
}
|
||||
end)(App)
|
||||
end
|
||||
|
||||
local function mapStateToProps(state, props)
|
||||
return { visible = state.isMenuOpen }
|
||||
end
|
||||
|
||||
return RoactRodux.connect(mapStateToProps, nil)(App)
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local InGameMenuPolicy = require(InGameMenu.InGameMenuPolicy)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local App = require(script.Parent.App)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
InGameMenuPolicy = Roact.createElement(InGameMenuPolicy.Provider, {
|
||||
policy = { InGameMenuPolicy.Mapper },
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
App = Roact.createElement(App),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
--[[
|
||||
Displays an image from the asset dictionary, slicing it if the asset
|
||||
requires that.
|
||||
]]
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Cryo = InGameMenuDependencies.Cryo
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
local ImageSetButton = UIBlox.Core.ImageSet.Button
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
|
||||
local function makeAssetImageComponent(component)
|
||||
return function(props)
|
||||
local imageKey = props.imageKey
|
||||
local imageData = Assets.Images[imageKey]
|
||||
|
||||
local imageProps
|
||||
if typeof(imageData) == "string" then
|
||||
imageProps = {
|
||||
Image = imageData,
|
||||
}
|
||||
else
|
||||
imageProps = imageData
|
||||
end
|
||||
|
||||
local mergedProps = Cryo.Dictionary.join(props, {
|
||||
imageKey = Cryo.None,
|
||||
}, imageProps, {
|
||||
BackgroundTransparency = 1,
|
||||
})
|
||||
|
||||
return Roact.createElement(component, mergedProps)
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
Label = makeAssetImageComponent(ImageSetLabel),
|
||||
Button = makeAssetImageComponent(ImageSetButton),
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local BarOnTopScrollingFrame = Roact.PureComponent:extend("BarOnTopScrollingFrame")
|
||||
|
||||
BarOnTopScrollingFrame.validateProps = t.strictInterface({
|
||||
Size = t.UDim2,
|
||||
Position = t.optional(t.UDim2),
|
||||
CanvasSize = t.UDim2,
|
||||
scrollBarOffset = t.integer,
|
||||
ScrollingEnabled = t.optional(t.boolean),
|
||||
|
||||
[Roact.Children] = t.table,
|
||||
})
|
||||
|
||||
BarOnTopScrollingFrame.defaultProps = {
|
||||
ScrollingEnabled = true,
|
||||
}
|
||||
|
||||
function BarOnTopScrollingFrame:init()
|
||||
self.state = {
|
||||
canvasPosition = Vector2.new(0, 0),
|
||||
}
|
||||
end
|
||||
|
||||
function BarOnTopScrollingFrame:render()
|
||||
return Roact.createElement("Frame", {
|
||||
Size = self.props.Size,
|
||||
Position = self.props.Position,
|
||||
BackgroundTransparency = 1,
|
||||
ClipsDescendants = true,
|
||||
}, {
|
||||
DisplayScrollingFrame = Roact.createElement("ScrollingFrame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, -self.props.scrollBarOffset, 1, 0),
|
||||
CanvasSize = self.props.CanvasSize - UDim2.new(0, self.props.scrollBarOffset, 0, 0),
|
||||
ScrollBarThickness = 8,
|
||||
BorderSizePixel = 0,
|
||||
ZIndex = 2,
|
||||
ScrollingEnabled = self.props.ScrollingEnabled,
|
||||
|
||||
[Roact.Change.CanvasPosition] = function(rbx)
|
||||
self:setState({
|
||||
CanvasPosition = rbx.CanvasPosition
|
||||
})
|
||||
end,
|
||||
}),
|
||||
|
||||
ContentsScrollingFrame = Roact.createElement("ScrollingFrame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, -self.props.scrollBarOffset, 1, 0),
|
||||
CanvasSize = self.props.CanvasSize - UDim2.new(0, self.props.scrollBarOffset, 0, 0),
|
||||
CanvasPosition = self.state.CanvasPosition,
|
||||
ScrollBarThickness = 0,
|
||||
BorderSizePixel = 0,
|
||||
ClipsDescendants = false,
|
||||
ScrollingEnabled = false,
|
||||
}, {
|
||||
Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, self.props.scrollBarOffset, 1, 0),
|
||||
}, self.props[Roact.Children]),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return BarOnTopScrollingFrame
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local BlurredModalPortal = Roact.PureComponent:extend("BlurredModalPortal")
|
||||
|
||||
BlurredModalPortal.validateProps = t.strictInterface({
|
||||
Enabled = t.boolean,
|
||||
[Roact.Children] = t.optional(t.table)
|
||||
})
|
||||
|
||||
function BlurredModalPortal:render()
|
||||
return Roact.createElement(Roact.Portal, {
|
||||
target = CoreGui
|
||||
}, {
|
||||
InGameMenuModalBlur = Roact.createElement("ScreenGui", {
|
||||
OnTopOfCoreBlur = true,
|
||||
Enabled = self.props.Enabled,
|
||||
IgnoreGuiInset = true,
|
||||
}, self.props[Roact.Children])
|
||||
})
|
||||
end
|
||||
|
||||
function BlurredModalPortal:didUpdate(prevProps)
|
||||
local wasEnabled = prevProps.Enabled
|
||||
local enabled = self.props.Enabled
|
||||
|
||||
if wasEnabled ~= enabled then
|
||||
RunService:SetRobloxGuiFocused(enabled)
|
||||
end
|
||||
end
|
||||
|
||||
return BlurredModalPortal
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RunService = game:GetService("RunService")
|
||||
local TextService = game:GetService("TextService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
|
||||
local ThemedTextLabel = require(script.Parent.ThemedTextLabel)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local MODAL_CONFIRM_ACTION = "InGameMenuModalConfirm"
|
||||
local MODAL_CANCEL_ACTION = "InGameMenuModalCancel"
|
||||
|
||||
local ConfirmationDialog = Roact.PureComponent:extend("ConfirmationDialog")
|
||||
|
||||
ConfirmationDialog.validateProps = t.strictInterface({
|
||||
bodyText = t.string,
|
||||
cancelText = t.string,
|
||||
confirmText = t.string,
|
||||
titleText = t.string,
|
||||
|
||||
bindReturnToConfirm = t.boolean,
|
||||
|
||||
onCancel = t.callback,
|
||||
onConfirm = t.callback,
|
||||
|
||||
blurBackground = t.boolean,
|
||||
|
||||
visible = t.boolean,
|
||||
})
|
||||
|
||||
ConfirmationDialog.defaultProps = {
|
||||
bodyText = "This is some body text. It will wrap when needed.",
|
||||
cancelText = "Cancel",
|
||||
confirmText = "Confirm",
|
||||
titleText = "Dialog Title",
|
||||
blurBackground = false,
|
||||
}
|
||||
|
||||
function ConfirmationDialog:render()
|
||||
local props = self.props
|
||||
|
||||
return withStyle(function(style)
|
||||
local bodyFont = style.Font.Body.Font
|
||||
local bodyFontSize = style.Font.Body.RelativeSize * style.Font.BaseSize
|
||||
local bodyText = props.bodyText
|
||||
local textHeight = TextService:GetTextSize(
|
||||
bodyText,
|
||||
bodyFontSize,
|
||||
bodyFont,
|
||||
-- 335 (width) - 20px padding on left and right
|
||||
Vector2.new(335 - 20 - 20, math.huge)
|
||||
).Y
|
||||
|
||||
-- 20px padding top, 20px padding bottom
|
||||
-- Minimum height of two lines of text.
|
||||
-- TODO: Line height!
|
||||
local bodyTextContainerHeight = 20 + math.max(textHeight, bodyFontSize * 2) + 20
|
||||
|
||||
return Roact.createElement(Roact.Portal, {
|
||||
target = CoreGui,
|
||||
}, {
|
||||
InGameMenuConfirmationDialog = Roact.createElement("ScreenGui", {
|
||||
DisplayOrder = 8,
|
||||
IgnoreGuiInset = true,
|
||||
OnTopOfCoreBlur = props.blurBackground,
|
||||
Enabled = props.visible,
|
||||
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||
}, {
|
||||
Overlay = Roact.createElement("TextButton", {
|
||||
AutoButtonColor = false,
|
||||
BackgroundColor3 = style.Theme.Overlay.Color,
|
||||
BackgroundTransparency = style.Theme.Overlay.Transparency,
|
||||
BorderSizePixel = 0,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Text = "",
|
||||
}),
|
||||
DialogMainFrame = Roact.createElement(ImageSetLabel, {
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.RoundedRect.Image,
|
||||
ImageColor3 = style.Theme.BackgroundUIDefault.Color,
|
||||
ImageTransparency = style.Theme.BackgroundUIDefault.Transparency,
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
ScaleType = Assets.Images.RoundedRect.ScaleType,
|
||||
-- button container (36px), bottom padding (20px)
|
||||
-- height: title bar (48px), divider (1px), body text height,
|
||||
Size = UDim2.new(0, 335, 0, 48 + 1 + bodyTextContainerHeight + 36 + 20),
|
||||
SliceCenter = Assets.Images.RoundedRect.SliceCenter,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingBottom = UDim.new(0, 20),
|
||||
PaddingLeft = UDim.new(0, 20),
|
||||
PaddingRight = UDim.new(0, 20),
|
||||
}),
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
Title = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Header1",
|
||||
themeKey = "TextEmphasis",
|
||||
|
||||
LayoutOrder = 1,
|
||||
Size = UDim2.new(1, 0, 0, 48),
|
||||
Text = props.titleText,
|
||||
}),
|
||||
Divider = Roact.createElement("Frame", {
|
||||
BackgroundColor3 = style.Theme.Divider.Color,
|
||||
BackgroundTransparency = style.Theme.Divider.Transparency,
|
||||
BorderSizePixel = 0,
|
||||
LayoutOrder = 3,
|
||||
Size = UDim2.new(0.8, 0, 0, 1),
|
||||
}),
|
||||
BodyTextContainer = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 4,
|
||||
Size = UDim2.new(1, 0, 0, bodyTextContainerHeight),
|
||||
}, {
|
||||
BodyText = Roact.createElement(ThemedTextLabel, {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Text = props.bodyText,
|
||||
TextWrapped = true,
|
||||
})
|
||||
}),
|
||||
ButtonContainer = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 5,
|
||||
Size = UDim2.new(1, 0, 0, 36),
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
Padding = UDim.new(0, 10),
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
}),
|
||||
CancelButton = Roact.createElement(UIBlox.App.Button.SecondaryButton, {
|
||||
layoutOrder = 1,
|
||||
size = UDim2.new(0.5, -5, 1, 0),
|
||||
text = props.cancelText,
|
||||
onActivated = props.onCancel,
|
||||
}),
|
||||
ConfirmButton = Roact.createElement(UIBlox.App.Button.PrimarySystemButton, {
|
||||
layoutOrder = 2,
|
||||
size = UDim2.new(0.5, -5, 1, 0),
|
||||
text = props.confirmText,
|
||||
onActivated = props.onConfirm,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
function ConfirmationDialog:updateBlur()
|
||||
local shouldBlur = self.props.blurBackground and self.props.visible
|
||||
RunService:SetRobloxGuiFocused(shouldBlur)
|
||||
end
|
||||
|
||||
-- TODO: consider if we want to have a button selection model like the old menu rather than enter always confirming
|
||||
function ConfirmationDialog:bindActions()
|
||||
local function confirmFunc(actionName, inputState, input)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
self.props.onConfirm()
|
||||
end
|
||||
end
|
||||
|
||||
if self.props.bindReturnToConfirm then
|
||||
ContextActionService:BindCoreAction(
|
||||
MODAL_CONFIRM_ACTION, confirmFunc, false, Enum.KeyCode.Return)
|
||||
end
|
||||
|
||||
local function cancelFunc(actionName, inputState, input)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
self.props.onCancel()
|
||||
end
|
||||
end
|
||||
|
||||
ContextActionService:BindCoreAction(
|
||||
MODAL_CANCEL_ACTION, cancelFunc, false, Enum.KeyCode.Escape)
|
||||
end
|
||||
|
||||
function ConfirmationDialog:unbindActions()
|
||||
ContextActionService:UnbindCoreAction(MODAL_CONFIRM_ACTION)
|
||||
ContextActionService:UnbindCoreAction(MODAL_CANCEL_ACTION)
|
||||
end
|
||||
|
||||
function ConfirmationDialog:didMount()
|
||||
self:updateBlur()
|
||||
|
||||
if self.props.visible then
|
||||
self:bindActions()
|
||||
end
|
||||
end
|
||||
|
||||
function ConfirmationDialog:didUpdate()
|
||||
self:updateBlur()
|
||||
|
||||
if self.props.visible then
|
||||
self:bindActions()
|
||||
else
|
||||
self:unbindActions()
|
||||
end
|
||||
end
|
||||
|
||||
function ConfirmationDialog:willUnmount()
|
||||
if self.props.blurBackground then
|
||||
RunService:SetRobloxGuiFocused(false)
|
||||
end
|
||||
self:unbindActions()
|
||||
end
|
||||
|
||||
return ConfirmationDialog
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
return function()
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local ConfirmationDialog = require(script.Parent.ConfirmationDialog)
|
||||
|
||||
local dummyDialogProps = {
|
||||
bodyText = "Hello world!",
|
||||
cancelText = "Cancel",
|
||||
confirmText = "Confirm",
|
||||
titleText = "Title",
|
||||
|
||||
bindReturnToConfirm = false,
|
||||
|
||||
onCancel = function()
|
||||
print("cancel")
|
||||
end,
|
||||
onConfirm = function()
|
||||
print("confirm")
|
||||
end,
|
||||
blurBackground = false,
|
||||
visible = true,
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
ConfirmationDialog = Roact.createElement(ConfirmationDialog, dummyDialogProps),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should be portaled into CoreGui", function()
|
||||
local element = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
ConfirmationDialog = Roact.createElement(ConfirmationDialog, dummyDialogProps),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
expect(CoreGui:FindFirstChild("InGameMenuConfirmationDialog")).to.never.equal(nil)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local SetFriendStatus = require(InGameMenu.Actions.SetFriendStatus)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
|
||||
local FriendStatusConnector = Roact.PureComponent:extend("FriendStatusConnector")
|
||||
|
||||
function FriendStatusConnector:init()
|
||||
self.state = {
|
||||
localPlayer = Players.LocalPlayer,
|
||||
}
|
||||
end
|
||||
|
||||
function FriendStatusConnector:render()
|
||||
local connections = {
|
||||
PlayerAdded = Roact.createElement(ExternalEventConnection, {
|
||||
event = Players.PlayerAdded,
|
||||
callback = function(player)
|
||||
if player == Players.LocalPlayer then
|
||||
self:setState({
|
||||
localPlayer = player,
|
||||
})
|
||||
else
|
||||
self.props.setFriendStatus(player.UserId, Enum.FriendStatus.Unknown)
|
||||
end
|
||||
end,
|
||||
}),
|
||||
|
||||
PlayerRemoving = Roact.createElement(ExternalEventConnection, {
|
||||
event = Players.PlayerRemoving,
|
||||
callback = function(player)
|
||||
self.props.setFriendStatus(player.UserId, nil)
|
||||
end,
|
||||
}),
|
||||
}
|
||||
|
||||
if self.state.localPlayer then
|
||||
connections.FriendStatusChanged = Roact.createElement(ExternalEventConnection, {
|
||||
event = Players.LocalPlayer.FriendStatusChanged,
|
||||
callback = function(player, friendStatus)
|
||||
self.props.setFriendStatus(player.UserId, friendStatus)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
return Roact.createFragment(connections)
|
||||
end
|
||||
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(nil, function(dispatch)
|
||||
return {
|
||||
setFriendStatus = function(player, friendStatus)
|
||||
return dispatch(SetFriendStatus(player, friendStatus))
|
||||
end,
|
||||
}
|
||||
end)(FriendStatusConnector)
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local SetInputType = require(InGameMenu.Actions.SetInputType)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local InputType = Constants.InputType
|
||||
|
||||
local LastInputTypeConnector = Roact.PureComponent:extend("LastInputTypeConnector")
|
||||
|
||||
local inputTypeMap = {
|
||||
[Enum.UserInputType.MouseButton2] = InputType.MouseAndKeyBoard,
|
||||
[Enum.UserInputType.MouseButton3] = InputType.MouseAndKeyBoard,
|
||||
[Enum.UserInputType.MouseWheel] = InputType.MouseAndKeyBoard,
|
||||
[Enum.UserInputType.MouseMovement] = InputType.MouseAndKeyBoard,
|
||||
[Enum.UserInputType.Keyboard] = InputType.MouseAndKeyBoard,
|
||||
|
||||
[Enum.UserInputType.Gamepad1] = InputType.Gamepad,
|
||||
[Enum.UserInputType.Gamepad2] = InputType.Gamepad,
|
||||
[Enum.UserInputType.Gamepad3] = InputType.Gamepad,
|
||||
[Enum.UserInputType.Gamepad4] = InputType.Gamepad,
|
||||
[Enum.UserInputType.Gamepad5] = InputType.Gamepad,
|
||||
[Enum.UserInputType.Gamepad6] = InputType.Gamepad,
|
||||
[Enum.UserInputType.Gamepad7] = InputType.Gamepad,
|
||||
[Enum.UserInputType.Gamepad8] = InputType.Gamepad,
|
||||
|
||||
[Enum.UserInputType.Touch] = InputType.Touch,
|
||||
}
|
||||
|
||||
function LastInputTypeConnector:init()
|
||||
local initalInputType = inputTypeMap[UserInputService:GetLastInputType()]
|
||||
if initalInputType then
|
||||
self.props.setInputType(initalInputType)
|
||||
end
|
||||
end
|
||||
|
||||
function LastInputTypeConnector:render()
|
||||
return Roact.createElement(ExternalEventConnection, {
|
||||
event = UserInputService.LastInputTypeChanged,
|
||||
callback = function(lastInputType)
|
||||
local inputType = inputTypeMap[lastInputType]
|
||||
if inputType then
|
||||
self.props.setInputType(inputType)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(nil, function(dispatch)
|
||||
return {
|
||||
setInputType = function(inputType)
|
||||
return dispatch(SetInputType(inputType))
|
||||
end,
|
||||
}
|
||||
end)(LastInputTypeConnector)
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
local VRService = game:GetService("VRService")
|
||||
|
||||
local Symbol = require(CorePackages.Symbol)
|
||||
local MouseIconOverrideService = require(CorePackages.InGameServices.MouseIconOverrideService)
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
|
||||
local INGAME_MENU_CUSOR_OVERRIDE_KEY = Symbol.named("InGameMenuCursorOverride")
|
||||
|
||||
local FFlagNewInGameMenuUseMouseOverrideService2 = game:DefineFastFlag("NewInGameMenuUseMouseOverrideService2", false)
|
||||
|
||||
local InputType = Constants.InputType
|
||||
|
||||
local MouseOverrideManager = Roact.PureComponent:extend("MouseOverrideManager")
|
||||
|
||||
MouseOverrideManager.validateProps = t.strictInterface({
|
||||
menuOpen = t.boolean,
|
||||
inputType = t.string,
|
||||
})
|
||||
|
||||
function MouseOverrideManager:render()
|
||||
return Roact.createElement(ExternalEventConnection, {
|
||||
event = UserInputService:GetPropertyChangedSignal("OverrideMouseIconBehavior"),
|
||||
callback = function()
|
||||
-- Another menu that overrides the mouse icon behaviour has closed.
|
||||
-- But we are still open so we need to keep re-override the icon behaviour.
|
||||
if self.props.menuOpen and UserInputService.OverrideMouseIconBehavior == Enum.OverrideMouseIconBehavior.None then
|
||||
self:updateMouseIconOverride()
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function MouseOverrideManager:init()
|
||||
self.didOverrideMouse = false
|
||||
end
|
||||
|
||||
if FFlagNewInGameMenuUseMouseOverrideService2 then
|
||||
function MouseOverrideManager:updateMouseIconOverride()
|
||||
if self.props.menuOpen then
|
||||
self.didOverrideMouse = true
|
||||
if self.props.inputType == InputType.Gamepad or VRService.VREnabled then
|
||||
MouseIconOverrideService.push(INGAME_MENU_CUSOR_OVERRIDE_KEY, Enum.OverrideMouseIconBehavior.ForceHide)
|
||||
else
|
||||
MouseIconOverrideService.push(INGAME_MENU_CUSOR_OVERRIDE_KEY, Enum.OverrideMouseIconBehavior.ForceShow)
|
||||
end
|
||||
elseif self.didOverrideMouse then
|
||||
self.didOverrideMouse = false
|
||||
MouseIconOverrideService.pop(INGAME_MENU_CUSOR_OVERRIDE_KEY)
|
||||
end
|
||||
end
|
||||
else
|
||||
function MouseOverrideManager:updateMouseIconOverride()
|
||||
if self.props.menuOpen then
|
||||
if self.props.inputType == InputType.Gamepad or VRService.VREnabled then
|
||||
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
|
||||
else
|
||||
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceShow
|
||||
end
|
||||
else
|
||||
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.None
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MouseOverrideManager:didMount()
|
||||
self:updateMouseIconOverride()
|
||||
end
|
||||
|
||||
function MouseOverrideManager:didUpdate()
|
||||
self:updateMouseIconOverride()
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(function(state)
|
||||
return {
|
||||
menuOpen = state.isMenuOpen,
|
||||
inputType = state.displayOptions.inputType,
|
||||
}
|
||||
end, nil)(MouseOverrideManager)
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local AppStorageService = game:GetService("AppStorageService")
|
||||
local RbxAnalyticsService = game:GetService("RbxAnalyticsService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local OpenNativeClosePrompt = require(InGameMenu.Actions.OpenNativeClosePrompt)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local InGameMenuPolicy = require(InGameMenu.InGameMenuPolicy)
|
||||
local UserLocalStore = require(InGameMenu.Utility.UserLocalStore)
|
||||
|
||||
local GetFFlagPlayerSpecificPopupCounter = require(InGameMenu.Flags.GetFFlagPlayerSpecificPopupCounter)
|
||||
|
||||
local LOCAL_STORAGE_KEY_NATIVE_CLOSE = "NativeCloseLuaPromptDisplayCount"
|
||||
local notificationTypes = GuiService:GetNotificationTypeList()
|
||||
|
||||
local NativeCloseEventConnector = Roact.PureComponent:extend("NativeCloseEventConnector")
|
||||
|
||||
NativeCloseEventConnector.validateProps = t.strictInterface({
|
||||
openNativeClosePrompt = t.callback,
|
||||
isEducationalPopupEnabled = t.boolean,
|
||||
maxDisplayCount = t.integer,
|
||||
})
|
||||
|
||||
function NativeCloseEventConnector:init()
|
||||
local function getDisplayCount()
|
||||
local localStore = AppStorageService
|
||||
if GetFFlagPlayerSpecificPopupCounter() then
|
||||
localStore = UserLocalStore.new()
|
||||
end
|
||||
return tonumber(localStore:GetItem(LOCAL_STORAGE_KEY_NATIVE_CLOSE)) or 0
|
||||
end
|
||||
|
||||
local function setDisplayCount(number)
|
||||
local localStore = AppStorageService
|
||||
if GetFFlagPlayerSpecificPopupCounter() then
|
||||
localStore = UserLocalStore.new()
|
||||
end
|
||||
localStore:SetItem(LOCAL_STORAGE_KEY_NATIVE_CLOSE, tostring(number))
|
||||
localStore:flush()
|
||||
end
|
||||
|
||||
self.onNativeCloseConnect = function()
|
||||
if self.props.isEducationalPopupEnabled then
|
||||
RbxAnalyticsService:ReportCounter("LeaveGame_NativeClose", 1)
|
||||
|
||||
if self.props.isClosingApp then
|
||||
GuiService:BroadcastNotification("", notificationTypes.NATIVE_EXIT)
|
||||
return
|
||||
end
|
||||
|
||||
local displayCount = getDisplayCount()
|
||||
local maxDisplayCount = self.props.maxDisplayCount
|
||||
|
||||
if displayCount >= maxDisplayCount then
|
||||
GuiService:BroadcastNotification("", notificationTypes.NATIVE_EXIT)
|
||||
else
|
||||
self.props.openNativeClosePrompt()
|
||||
setDisplayCount(displayCount + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function NativeCloseEventConnector:render()
|
||||
return Roact.createElement(ExternalEventConnection, {
|
||||
event = GuiService.NativeClose,
|
||||
callback = self.onNativeCloseConnect,
|
||||
})
|
||||
end
|
||||
|
||||
NativeCloseEventConnector = InGameMenuPolicy.connect(function(appPolicy, props)
|
||||
return {
|
||||
isEducationalPopupEnabled = appPolicy.enableEducationalPopup(),
|
||||
maxDisplayCount = appPolicy.educationalPopupMaxDisplayCount(),
|
||||
}
|
||||
end)(NativeCloseEventConnector)
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(function(state, props)
|
||||
return {
|
||||
isClosingApp = state.nativeClosePrompt.closingApp,
|
||||
}
|
||||
end, function(dispatch)
|
||||
return {
|
||||
openNativeClosePrompt = function()
|
||||
return dispatch(OpenNativeClosePrompt())
|
||||
end,
|
||||
}
|
||||
end)(NativeCloseEventConnector)
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
local InGameMenuPolicy = require(InGameMenu.InGameMenuPolicy)
|
||||
|
||||
local mockEducationalPolicyMapper = function(policy)
|
||||
return {
|
||||
enableEducationalPopup = function()
|
||||
return false
|
||||
end,
|
||||
educationalPopupMaxDisplayCount = function()
|
||||
return 0
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
local NativeCloseEventConnector = require(script.Parent.NativeCloseEventConnector)
|
||||
|
||||
it("should create and destroy without errors on engine feature enabled", function()
|
||||
local isNativeCloseIntercept = game:GetEngineFeature("NativeCloseIntercept")
|
||||
--[[
|
||||
NativeCloseEventConnector relies on GuiService.NativeClose which guarded
|
||||
by an engine feature NativeCloseIntercept
|
||||
Skip unit test for older engine version which NativeCloseIntercept is disabled
|
||||
]]
|
||||
if not isNativeCloseIntercept then
|
||||
SKIP()
|
||||
return
|
||||
end
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
PolicyProvider = Roact.createElement(InGameMenuPolicy.Provider, {
|
||||
policy = { mockEducationalPolicyMapper },
|
||||
}, {
|
||||
TestComponent = Roact.createElement(NativeCloseEventConnector, {
|
||||
openNativeClosePrompt = function() end,
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
--!nocheck
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local GameSettings = settings():WaitForChild("Game Options") -- TODO: we should migrate the APIs from here to CoreGui
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
|
||||
local SetVideoRecording = require(InGameMenu.Actions.SetVideoRecording)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
|
||||
local VideoRecordConnector = Roact.PureComponent:extend("VideoRecordConnector")
|
||||
|
||||
function VideoRecordConnector:init()
|
||||
self.props.setVideoRecording(GameSettings.VideoRecording)
|
||||
end
|
||||
|
||||
function VideoRecordConnector:render()
|
||||
return Roact.createElement(ExternalEventConnection, {
|
||||
event = GameSettings:GetPropertyChangedSignal("VideoRecording"),
|
||||
callback = function ()
|
||||
self.props.setVideoRecording(GameSettings.VideoRecording)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(nil, function (dispatch)
|
||||
return {
|
||||
setVideoRecording = function (state)
|
||||
return dispatch(SetVideoRecording(state))
|
||||
end,
|
||||
}
|
||||
end)(VideoRecordConnector)
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local FriendStatusConnector = require(script.FriendStatusConnector)
|
||||
local LastInputTypeConnector = require(script.LastInputTypeConnector)
|
||||
local MouseOverrideManager = require(script.MouseOverrideManager)
|
||||
local VideoRecordConnector = require(script.VideoRecordConnector)
|
||||
local NativeCloseEventConnector = require(script.NativeCloseEventConnector)
|
||||
|
||||
local RobloxGui = game:GetService("CoreGui"):WaitForChild("RobloxGui")
|
||||
local InGameMenuPolicy = require(RobloxGui.Modules.InGameMenu.InGameMenuPolicy)
|
||||
local GetFFlagUseRoactPolicyProvider = require(RobloxGui.Modules.Flags.GetFFlagUseRoactPolicyProvider)
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
isEducationalPopupEnabled = t.optional(t.boolean),
|
||||
})
|
||||
|
||||
local function Connection(props)
|
||||
assert(validateProps)
|
||||
return Roact.createFragment({
|
||||
FriendStatusConnector = Roact.createElement(FriendStatusConnector),
|
||||
LastInputTypeConnector = Roact.createElement(LastInputTypeConnector),
|
||||
MouseOverrideManager = Roact.createElement(MouseOverrideManager),
|
||||
VideoRecordConnector = Roact.createElement(VideoRecordConnector),
|
||||
NativeCloseEventConnector = props.isEducationalPopupEnabled and Roact.createElement(NativeCloseEventConnector)
|
||||
or nil,
|
||||
})
|
||||
end
|
||||
|
||||
if GetFFlagUseRoactPolicyProvider() then
|
||||
Connection = InGameMenuPolicy.connect(function(appPolicy, props)
|
||||
return {
|
||||
isEducationalPopupEnabled = appPolicy.enableEducationalPopup(),
|
||||
}
|
||||
end)(Connection)
|
||||
end
|
||||
|
||||
return Connection
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
local KeyLabel = require(InGameMenu.Components.KeyLabel)
|
||||
local PointerLabel = require(InGameMenu.Components.PointerLabel)
|
||||
local Divider = require(InGameMenu.Components.Divider)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local CONTROL_ENTRY_HEIGHT = 50
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
actionDescription = t.string,
|
||||
inputs = t.table,
|
||||
isList = t.optional(t.boolean),
|
||||
hasDivider = t.boolean,
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
local function renderHorizontalSpacer(symbol, layoutOrder, isElipses)
|
||||
local size = isElipses and UDim2.new(0, 19, 0, 24) or UDim2.new(0, 19, 0, 16)
|
||||
local yAlignment = isElipses and Enum.TextYAlignment.Bottom
|
||||
|
||||
return Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextEmphasis",
|
||||
fontKey = "Header1",
|
||||
Text = symbol,
|
||||
Size = size,
|
||||
TextYAlignment = yAlignment,
|
||||
LayoutOrder = layoutOrder,
|
||||
})
|
||||
end
|
||||
|
||||
local function renderKey(input, layoutOrder)
|
||||
local inputComponent = input.EnumType == Enum.KeyCode and KeyLabel or PointerLabel
|
||||
return Roact.createElement(inputComponent, {
|
||||
input = input,
|
||||
LayoutOrder = layoutOrder,
|
||||
})
|
||||
end
|
||||
|
||||
local function renderInputIconContainer(inputs, isList)
|
||||
local children = {}
|
||||
|
||||
-- If we're rendering inputs as a "list", we render the keys next to
|
||||
-- eachother, with a "..." at the end, so we want 0 padding.
|
||||
local paddingWidth = not isList and 7 or 0
|
||||
|
||||
children.ListLayout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, paddingWidth),
|
||||
})
|
||||
|
||||
local layoutOrder = 1
|
||||
for i, input in ipairs(inputs) do
|
||||
|
||||
-- If input is a group of inputs, we iterate over that table and render
|
||||
-- with "+"" as spacers. Otherwise, we render the single input with "/"
|
||||
-- as the spacer.
|
||||
if typeof(input) ~= "table" then
|
||||
children["input"..i] = renderKey(input, layoutOrder)
|
||||
layoutOrder = layoutOrder + 1
|
||||
|
||||
-- if "list", append elipses to the end of inputs
|
||||
if isList and i == #inputs then
|
||||
children.Elipses = renderHorizontalSpacer("...", layoutOrder, true)
|
||||
end
|
||||
else
|
||||
for i2, groupedInput in ipairs(input) do
|
||||
local order = string.format("%d_%d", i, i2)
|
||||
children["groupedInput"..order] = renderKey(groupedInput, layoutOrder)
|
||||
layoutOrder = layoutOrder + 1
|
||||
|
||||
if i2 < #input then
|
||||
children["groupedDivider"..order] = renderHorizontalSpacer("+", layoutOrder)
|
||||
layoutOrder = layoutOrder + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If "list", don't render dividers
|
||||
if not isList and i < #inputs then
|
||||
children["divider"..i] = renderHorizontalSpacer("/", layoutOrder)
|
||||
layoutOrder = layoutOrder + 1
|
||||
end
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, 0, 1, 0),
|
||||
Position = UDim2.new(1, 0, 0, 0),
|
||||
}, children)
|
||||
end
|
||||
|
||||
local function ControlEntry(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withLocalization({
|
||||
actionDescription = props.actionDescription,
|
||||
})(function(localized)
|
||||
return Roact.createElement("Frame", {
|
||||
LayoutOrder = props.LayoutOrder,
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, CONTROL_ENTRY_HEIGHT)
|
||||
}, {
|
||||
ActionLabel = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Header2",
|
||||
themeKey = "TextEmphasis",
|
||||
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Size = UDim2.new(0, 200, 0, 22),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Position = UDim2.new(0, 0, 0.5, 0),
|
||||
|
||||
Text = localized.actionDescription,
|
||||
}),
|
||||
Divider = props.hasDivider and Roact.createElement(Divider, {
|
||||
Size = UDim2.new(1, 0, 0, 1),
|
||||
Position = UDim2.new(0, 0, 1, -1),
|
||||
}),
|
||||
Inputs = renderInputIconContainer(props.inputs, props.isList),
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
return ControlEntry
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local ControlEntry = require(script.Parent.ControlEntry)
|
||||
|
||||
local key = Enum.KeyCode
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
ControlEntry = Roact.createElement(ControlEntry, {
|
||||
actionDescription = "CoreScripts.InGameMenu.Controls.MoveForward",
|
||||
inputs = { key.A },
|
||||
isList = false,
|
||||
hasDivider = false,
|
||||
LayoutOrder = 5,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should create and destroy without errors when displaying a list", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
ControlEntry = Roact.createElement(ControlEntry, {
|
||||
actionDescription = "CoreScripts.InGameMenu.Controls.MoveForward",
|
||||
inputs = { key.A, key.B, key.C, key.D },
|
||||
isList = true,
|
||||
hasDivider = true,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should create and destroy without errors when displaying multiple key inputs", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
ControlEntry = Roact.createElement(ControlEntry, {
|
||||
actionDescription = "CoreScripts.InGameMenu.Controls.MoveForward",
|
||||
inputs = { {key.LeftSuper, key.LeftShift, key.Three} },
|
||||
isList = false,
|
||||
hasDivider = false,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local HEADER_PADDING_TOP = 20
|
||||
local HEADER_TEXT_HEIGHT = 17
|
||||
local HEADER_PADDING_BOTTOM = 8
|
||||
|
||||
local HEADER_HEIGHT = HEADER_PADDING_TOP + HEADER_TEXT_HEIGHT + HEADER_PADDING_BOTTOM
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
title = t.string,
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
local function ControlEntry(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withLocalization({
|
||||
title = props.title,
|
||||
})(function(localized)
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, HEADER_HEIGHT),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = props.LayoutOrder,
|
||||
}, {
|
||||
HeaderText = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "CaptionHeader",
|
||||
themeKey = "TextMuted",
|
||||
|
||||
Text = localized.title,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Position = UDim2.new(0, 0, 0, HEADER_PADDING_TOP),
|
||||
Size = UDim2.new(1, 0, 0, HEADER_TEXT_HEIGHT),
|
||||
})
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
return ControlEntry
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local ControlEntryHeader = require(script.Parent.ControlEntryHeader)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
ControlEntryHeader = Roact.createElement(ControlEntryHeader, {
|
||||
title = "CoreScripts.InGameMenu.Controls.CharacterMovementTitle",
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local ThemedTextLabel = require(script.Parent.Parent.ThemedTextLabel)
|
||||
local Divider = require(script.Parent.Parent.Divider)
|
||||
-- local KeyLabel = require(script.Parent.KeyLabel)
|
||||
|
||||
local SetCurrentPage = require(InGameMenu.Actions.SetCurrentPage)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local ImageSetButton = UIBlox.Core.ImageSet.Button
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local HEADER_HEIGHT = 132
|
||||
|
||||
local HEADER_SIDE_PADDING = 95
|
||||
|
||||
local HEADER_CONTENT_Y_CENTER = 100
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
titleText = t.string,
|
||||
[Roact.Children] = t.optional(t.table),
|
||||
onClosed = t.callback,
|
||||
})
|
||||
|
||||
local function ControlLayoutContainer(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withLocalization({
|
||||
titleText = props.titleText,
|
||||
})(function(localized)
|
||||
return withStyle(function(style)
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundColor3 = style.Theme.Overlay.Color,
|
||||
BackgroundTransparency = style.Theme.Overlay.Transparency,
|
||||
Active = true,
|
||||
}, {
|
||||
|
||||
PageContent = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, -HEADER_HEIGHT),
|
||||
Position = UDim2.new(0, 0, 0, HEADER_HEIGHT),
|
||||
ClipsDescendants = true
|
||||
}, props[Roact.Children]),
|
||||
|
||||
HeaderContainer = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, HEADER_HEIGHT),
|
||||
BackgroundTransparency = 1,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, HEADER_SIDE_PADDING),
|
||||
PaddingRight = UDim.new(0, HEADER_SIDE_PADDING),
|
||||
}),
|
||||
CloseButton = Roact.createElement(ImageSetButton, {
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Position = UDim2.new(0, 0, 0, HEADER_CONTENT_Y_CENTER),
|
||||
|
||||
Size = UDim2.new(0, 36, 0, 36),
|
||||
Image = Assets.Images.CloseModal,
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
[Roact.Event.Activated] = props.onClosed,
|
||||
}),
|
||||
HeaderTextLabel = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Header1",
|
||||
themeKey = "TextEmphasis",
|
||||
Size = UDim2.new(0, 250, 0, 28),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Position = UDim2.new(0.5, 0, 0, HEADER_CONTENT_Y_CENTER),
|
||||
Text = localized.titleText,
|
||||
}),
|
||||
HeaderDivider = Roact.createElement(Divider, {
|
||||
Size = UDim2.new(1, 0, 0, 2),
|
||||
AnchorPoint = Vector2.new(0.5, 1),
|
||||
Position = UDim2.new(0.5, 0, 1, 0),
|
||||
}),
|
||||
-- Yale wanted to keep this around, unsure if we're going to use it or not
|
||||
-- CloseButtonLabel = Roact.createElement(KeyLabel, {
|
||||
-- Position = UDim2.new(0, 36, 0, 0),
|
||||
-- input = Enum.KeyCode.Escape,
|
||||
-- }),
|
||||
}),
|
||||
|
||||
RadialGlowBig = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.RadialGlow,
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
|
||||
-- Renders as a square based off of screen width
|
||||
SizeConstraint = Enum.SizeConstraint.RelativeXX,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
}),
|
||||
RadialGlowSquashed = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.RadialGlow,
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Position = UDim2.new(0.5, 0, 1, 0),
|
||||
|
||||
-- Renders with height always at 60% of width
|
||||
SizeConstraint = Enum.SizeConstraint.RelativeXX,
|
||||
Size = UDim2.new(1, 0, 0.6, 0),
|
||||
})
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
nil,
|
||||
function(dispatch)
|
||||
return {
|
||||
onClosed = function()
|
||||
dispatch(SetCurrentPage(Constants.defaultPageKey))
|
||||
end
|
||||
}
|
||||
end
|
||||
)(ControlLayoutContainer)
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local ControlLayoutContainer = require(script.Parent.ControlLayoutContainer)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
ControlEntryHeader = Roact.createElement(ControlLayoutContainer, {
|
||||
titleText = "CoreScripts.InGameMenu.Controls.GamepadTitle",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local Controls = require(InGameMenu.Resources.Controls)
|
||||
|
||||
local SetControlLayout = require(InGameMenu.Actions.SetControlLayout)
|
||||
|
||||
local ControlLayoutSetter = Roact.PureComponent:extend("ControlLayoutSetter")
|
||||
|
||||
function ControlLayoutSetter:render()
|
||||
return nil
|
||||
end
|
||||
|
||||
function ControlLayoutSetter:didMount()
|
||||
self.inputChangedConnection = UserInputService.LastInputTypeChanged:Connect(function(lastInputType)
|
||||
|
||||
local controlLayout = Controls.inputToControlTypeMap[lastInputType]
|
||||
|
||||
if controlLayout then
|
||||
self.props.dispatchSetControlLayout(controlLayout)
|
||||
end
|
||||
end)
|
||||
|
||||
self.props.dispatchSetControlLayout(Controls.ControlLayouts.TOUCH)
|
||||
end
|
||||
|
||||
function ControlLayoutSetter:willUnmount()
|
||||
if self.inputChangedConnection then
|
||||
self.inputChangedConnection:Disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
nil,
|
||||
function(dispatch)
|
||||
return {
|
||||
dispatchSetControlLayout = function(controlLayout)
|
||||
dispatch(SetControlLayout(controlLayout))
|
||||
end
|
||||
}
|
||||
end
|
||||
)(ControlLayoutSetter)
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent.Parent
|
||||
|
||||
local ControlLayoutContainer = require(script.Parent.Parent.ControlLayoutContainer)
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
local Controls = require(InGameMenu.Resources.Controls)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local GAMEPAD_IMAGE_HEIGHT = 465
|
||||
|
||||
local GAMEPAD_IMAGE_WIDTH = 473
|
||||
local GAMEPAD_WIDTH = 440
|
||||
local GAMEPAD_ICONS_WIDTH = GAMEPAD_IMAGE_WIDTH - GAMEPAD_WIDTH
|
||||
|
||||
-- Height and padding for gamepad button labels
|
||||
local TEXT_HEIGHT = 22
|
||||
local LIST_PADDING = 21
|
||||
|
||||
-- Offsets for labels
|
||||
local TEXT_Y_OFFSET = 62
|
||||
local TEXT_X_PADDING = 26
|
||||
|
||||
-- Text for camera controls needs be offset based on the other text on the right
|
||||
-- side of the controller. We mutliply the number of control items by the total
|
||||
-- size (height + padding) of each control label element.
|
||||
local RIGHT_LABELS_HEIGHT = (#Controls.gamepadLabels.right * (TEXT_HEIGHT + LIST_PADDING))
|
||||
local CONTROL_CAMERA_TEXT_Y_OFFSET = RIGHT_LABELS_HEIGHT + TEXT_Y_OFFSET
|
||||
|
||||
local function verticalControlsList(props)
|
||||
return withLocalization(props.controls)(function(localized)
|
||||
local listChildren = {}
|
||||
|
||||
listChildren.ListLayout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, LIST_PADDING),
|
||||
})
|
||||
|
||||
for i, control in ipairs(localized) do
|
||||
listChildren["controlLabel_"..i] = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Header2",
|
||||
themeKey = "TextEmphasis",
|
||||
TextXAlignment = props.TextXAlignment,
|
||||
Size = UDim2.new(0, 1, 0, TEXT_HEIGHT),
|
||||
Text = control,
|
||||
LayoutOrder = i,
|
||||
})
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Position = props.Position,
|
||||
|
||||
Size = UDim2.new(0, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
}, listChildren)
|
||||
end)
|
||||
end
|
||||
|
||||
local function GamepadControls()
|
||||
local platform = UserInputService:GetPlatform()
|
||||
local isXbox = platform == Enum.Platform.XBox360 or platform == Enum.Platform.XBoxOne
|
||||
|
||||
return Roact.createElement(ControlLayoutContainer, {
|
||||
titleText = "CoreScripts.InGameMenu.Controls.GamepadTitle"
|
||||
}, {
|
||||
GamepadImage = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = isXbox and Assets.Images.GamepadXbox or Assets.Images.Gamepad,
|
||||
Size = UDim2.new(0, GAMEPAD_IMAGE_WIDTH, 0, GAMEPAD_IMAGE_HEIGHT),
|
||||
Position = UDim2.new(0.5, GAMEPAD_ICONS_WIDTH/2, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
}, {
|
||||
LeftControlsList = Roact.createElement(verticalControlsList, {
|
||||
TextXAlignment = Enum.TextXAlignment.Right,
|
||||
Position = UDim2.new(0, -TEXT_X_PADDING, 0, TEXT_Y_OFFSET),
|
||||
controls = Controls.gamepadLabels.left
|
||||
}),
|
||||
RightControlsList = Roact.createElement(verticalControlsList, {
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Position = UDim2.new(1, TEXT_X_PADDING-GAMEPAD_ICONS_WIDTH, 0, TEXT_Y_OFFSET),
|
||||
controls = Controls.gamepadLabels.right
|
||||
}),
|
||||
RightIconsControlslIst = Roact.createElement(verticalControlsList, {
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Position = UDim2.new(1, TEXT_X_PADDING, 0, CONTROL_CAMERA_TEXT_Y_OFFSET),
|
||||
controls = Controls.gamepadLabels.camera
|
||||
})
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return GamepadControls
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local GamepadControls = require(script.Parent.GamepadControls)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
GamepadControls = Roact.createElement(GamepadControls),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent.Parent
|
||||
|
||||
local Controls = require(InGameMenu.Resources.Controls)
|
||||
|
||||
local ControlLayoutContainer = require(script.Parent.Parent.ControlLayoutContainer)
|
||||
local PaddedScrollFrame = require(InGameMenu.Components.PaddedScrollFrame)
|
||||
local ControlEntry = require(script.Parent.Parent.ControlEntry)
|
||||
local ControlEntryHeader = require(script.Parent.Parent.ControlEntryHeader)
|
||||
|
||||
local CONTENT_MAX_WIDTH = 900
|
||||
local CONTENT_MIN_WIDTH = 150
|
||||
|
||||
local MIN_SIDE_PADDING = 95
|
||||
|
||||
local HEADER_HEIGHT = 45
|
||||
local ENTRY_HEIGHT = 50
|
||||
|
||||
local BOTTOM_PADDING = 130
|
||||
|
||||
local function KeyboardControls(props)
|
||||
local controlsChildren = {}
|
||||
controlsChildren.ListLayout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
})
|
||||
controlsChildren.Padding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, MIN_SIDE_PADDING),
|
||||
PaddingRight = UDim.new(0, MIN_SIDE_PADDING)
|
||||
})
|
||||
controlsChildren.SizeConstraint = Roact.createElement("UISizeConstraint", {
|
||||
MaxSize = Vector2.new(CONTENT_MAX_WIDTH + (MIN_SIDE_PADDING * 2), math.huge),
|
||||
MinSize = Vector2.new(CONTENT_MIN_WIDTH, 0),
|
||||
})
|
||||
|
||||
local numHeaders = 0
|
||||
local numEntries = 0
|
||||
|
||||
local layoutOrder = 1
|
||||
|
||||
for _, controlsGroup in ipairs(Controls.keybinds) do
|
||||
local groupTitle = controlsGroup.title
|
||||
numHeaders = numHeaders + 1
|
||||
|
||||
controlsChildren[groupTitle.."_header"] = Roact.createElement(ControlEntryHeader, {
|
||||
title = groupTitle,
|
||||
LayoutOrder = layoutOrder,
|
||||
})
|
||||
|
||||
layoutOrder = layoutOrder + 1
|
||||
|
||||
for controlIndex, controlAction in ipairs(controlsGroup.controls) do
|
||||
numEntries = numEntries + 1
|
||||
controlsChildren[groupTitle.."_"..controlIndex] = Roact.createElement(ControlEntry, {
|
||||
LayoutOrder = layoutOrder,
|
||||
actionDescription = controlAction.desc,
|
||||
inputs = controlAction.inputs,
|
||||
isList = controlAction.list,
|
||||
hasDivider = controlIndex ~= #controlsGroup.controls,
|
||||
})
|
||||
|
||||
layoutOrder = layoutOrder + 1
|
||||
end
|
||||
end
|
||||
|
||||
local canvasHeight = (HEADER_HEIGHT * numHeaders) + (ENTRY_HEIGHT * numEntries)
|
||||
|
||||
return Roact.createElement(ControlLayoutContainer, {
|
||||
titleText = "CoreScripts.InGameMenu.Controls.KeyboardAndMouseTitle",
|
||||
}, {
|
||||
ScrollContent = Roact.createElement(PaddedScrollFrame, {
|
||||
CanvasSize = UDim2.new(0, 0, 0, canvasHeight),
|
||||
ScrollingDirection = Enum.ScrollingDirection.Y,
|
||||
Size = UDim2.new(1, 0, 1, -BOTTOM_PADDING),
|
||||
}, {
|
||||
ListContainer = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
Position = UDim2.new(0.5, 0, 0, 0)
|
||||
}, controlsChildren)
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return KeyboardControls
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local KeyboardControls = require(script.Parent.KeyboardControls)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
KeyboardControls = Roact.createElement(KeyboardControls),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
|
||||
local KeyboardControls = require(script.Parent.KeyboardControls)
|
||||
|
||||
local function TouchControls(props)
|
||||
return Roact.createElement(KeyboardControls, props)
|
||||
end
|
||||
|
||||
return TouchControls
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local TouchControls = require(script.Parent.TouchControls)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
TouchControls = Roact.createElement(TouchControls),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local Controls = require(InGameMenu.Resources.Controls)
|
||||
|
||||
local KeyboardControls = require(script.ControlLayouts.KeyboardControls)
|
||||
local GamepadControls = require(script.ControlLayouts.GamepadControls)
|
||||
-- local TouchControls = require(InGameMenu.Components.ControlLayouts.TouchControls)
|
||||
|
||||
local ControlsPage = Roact.PureComponent:extend("ControlsPage")
|
||||
|
||||
function ControlsPage:render()
|
||||
local controlLayout = self.props.controlLayout
|
||||
|
||||
if controlLayout == Controls.ControlLayouts.KEYBOARD then
|
||||
return Roact.createElement(KeyboardControls)
|
||||
elseif controlLayout == Controls.ControlLayouts.GAMEPAD then
|
||||
return Roact.createElement(GamepadControls)
|
||||
-- elseif controlLayout == Controls.ControlLayouts.TOUCH then
|
||||
-- return Roact.createElement(TouchControls)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(function(state)
|
||||
local controlLayout = state.controlLayout
|
||||
|
||||
return {
|
||||
controlLayout = controlLayout
|
||||
}
|
||||
end)(ControlsPage)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local Cryo = InGameMenuDependencies.Cryo
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
Size = t.optional(t.UDim2),
|
||||
Position = t.optional(t.UDim2),
|
||||
AnchorPoint = t.optional(t.Vector2),
|
||||
LayoutOrder = t.optional(t.integer),
|
||||
Visible = t.optional(t.boolean),
|
||||
})
|
||||
|
||||
local function Divider(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withStyle(function(style)
|
||||
-- Divider relies on being right-aligned in order to be left-indented.
|
||||
return Roact.createElement("Frame", Cryo.Dictionary.join({
|
||||
Size = UDim2.new(1, 0, 0, 1),
|
||||
}, props, {
|
||||
BorderSizePixel = 0,
|
||||
BackgroundColor3 = style.Theme.Divider.Color,
|
||||
BackgroundTransparency = style.Theme.Divider.Transparency,
|
||||
}))
|
||||
end)
|
||||
end
|
||||
|
||||
return Divider
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
local divideTransparency = require(InGameMenu.Utility.divideTransparency)
|
||||
|
||||
local ThemedButton = require(script.Parent.ThemedButton)
|
||||
local ThemedTextLabel = require(script.Parent.ThemedTextLabel)
|
||||
local Divider = require(script.Parent.Divider)
|
||||
local BarOnTopScrollingFrame = require(script.Parent.BarOnTopScrollingFrame)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local DropDownSelection = Roact.PureComponent:extend("DropDownSelection")
|
||||
|
||||
local DROP_DOWN_BORDER_SIZE = 4
|
||||
local MAX_ITEMS_DISPLAYED = 7.5
|
||||
local ITEM_HEIGHT = 44
|
||||
|
||||
DropDownSelection.validateProps = t.intersection(t.strictInterface({
|
||||
Size = t.UDim2,
|
||||
Position = t.optional(t.UDim2),
|
||||
LayoutOrder = t.optional(t.integer),
|
||||
|
||||
placeHolderText = t.optional(t.string),
|
||||
selectedIndex = t.integer, --0 if placeholder is selected
|
||||
selections = t.array(t.string),
|
||||
localize = t.boolean,
|
||||
enabled = t.boolean,
|
||||
selectionChanged = t.callback,
|
||||
}), function(props)
|
||||
if props.selectedIndex < 0 then
|
||||
return false, "selectedIndex must be greater than 0"
|
||||
end
|
||||
|
||||
if props.selectedIndex > #props.selections then
|
||||
return false, "selectedIndex must not be greater than the number of selections"
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
function DropDownSelection:init()
|
||||
self.rootRef = Roact.createRef()
|
||||
|
||||
self.state = {
|
||||
isOpen = false,
|
||||
dropDownItemHeight = 10,
|
||||
absolutePositionX = 0,
|
||||
absolutePositionY = 0,
|
||||
|
||||
screenSizeX = 0,
|
||||
screenSizeY = 0,
|
||||
}
|
||||
|
||||
self.ancestryConnection = nil
|
||||
self.sizeConnection = nil
|
||||
end
|
||||
|
||||
function DropDownSelection:renderDropDownList(style, localized)
|
||||
local dropDownList = {}
|
||||
dropDownList.Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
})
|
||||
|
||||
for index, dropDownOption in ipairs(self.props.selections) do
|
||||
local buttonText = dropDownOption
|
||||
if self.props.localize then
|
||||
buttonText = localized[index]
|
||||
end
|
||||
|
||||
dropDownList["button" .. index] = Roact.createElement(ThemedButton, {
|
||||
Size = UDim2.new(1, 0, 0, self.state.dropDownItemHeight - 1),
|
||||
LayoutOrder = index * 2,
|
||||
onActivated = function()
|
||||
self:setState({
|
||||
isOpen = false,
|
||||
})
|
||||
self.props.selectionChanged(index)
|
||||
end,
|
||||
normalThemeKey = "BackgroundUIDefault",
|
||||
hoverThemeKey = "BackgroundOnHover",
|
||||
|
||||
imageProps = Assets.Images.WhiteSquare,
|
||||
|
||||
renderChildren = function(transparency, isHovered, isPressed)
|
||||
local iconColor = isHovered and style.Theme.IconOnHover or style.Theme.IconDefault
|
||||
local iconColor3 = iconColor.Color
|
||||
local iconTransparency = divideTransparency(iconColor.Transparency, isPressed and 2 or 1)
|
||||
|
||||
return {
|
||||
SelectedIcon = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
AnchorPoint = Vector2.new(1, 0.5),
|
||||
Size = UDim2.new(0, 15, 0, 12),
|
||||
Position = UDim2.new(1, -25, 0.5, 0),
|
||||
Image = Assets.Images.SelectedCheck,
|
||||
ImageColor3 = iconColor3,
|
||||
ImageTransparency = iconTransparency,
|
||||
Visible = self.props.selectedIndex == index
|
||||
}),
|
||||
|
||||
TextLabel = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Header2",
|
||||
themeKey = "TextEmphasis",
|
||||
|
||||
Position = UDim2.new(0, 25, 0, 0),
|
||||
Size = UDim2.new(1, -25, 1, 0),
|
||||
Text = buttonText,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
}),
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
if index < #self.props.selections then
|
||||
dropDownList["divider" .. index] = Roact.createElement(Divider, {
|
||||
LayoutOrder = index * 2 + 1
|
||||
})
|
||||
end
|
||||
end
|
||||
return dropDownList
|
||||
end
|
||||
|
||||
function DropDownSelection:render()
|
||||
return withStyle(function(style)
|
||||
return withLocalization(
|
||||
self.props.localize and self.props.selections or {}
|
||||
)(function(localized)
|
||||
local dropDownOpenText = self.props.placeHolderText
|
||||
if self.props.selectedIndex > 0 then
|
||||
if self.props.localize then
|
||||
dropDownOpenText = localized[self.props.selectedIndex]
|
||||
else
|
||||
dropDownOpenText = self.props.selections[self.props.selectedIndex]
|
||||
end
|
||||
end
|
||||
|
||||
local shadowSize = Assets.Images.Shadow.Size
|
||||
local itemSize = math.min(#self.props.selections, MAX_ITEMS_DISPLAYED) * ITEM_HEIGHT
|
||||
local dropDownList = self:renderDropDownList(style, localized)
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = self.props.Size,
|
||||
Position = self.props.Position,
|
||||
LayoutOrder = self.props.LayoutOrder,
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
[Roact.Ref] = self.rootRef,
|
||||
|
||||
[Roact.Change.AbsoluteSize] = function(rbx)
|
||||
self:setState({
|
||||
dropDownItemHeight = rbx.AbsoluteSize.Y,
|
||||
})
|
||||
end,
|
||||
|
||||
[Roact.Change.AbsolutePosition] = function(rbx)
|
||||
self:setState({
|
||||
absolutePositionX = rbx.AbsolutePosition.X,
|
||||
absolutePositionY = rbx.AbsolutePosition.Y,
|
||||
})
|
||||
end,
|
||||
}, {
|
||||
OpenDropDownButton = Roact.createElement(ThemedButton, {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Visible = not self.state.isOpen,
|
||||
enabled = self.props.enabled,
|
||||
onActivated = function()
|
||||
self:setState({
|
||||
isOpen = true,
|
||||
})
|
||||
end,
|
||||
normalThemeKey = "SecondaryDefault",
|
||||
hoverThemeKey = "SecondaryOnHover",
|
||||
imageProps = Assets.Images.RoundedRectBorder,
|
||||
renderChildren = function(transparency, isHovered, isPressed)
|
||||
local iconColor = isHovered and style.Theme.IconOnHover or style.Theme.IconDefault
|
||||
local iconColor3 = iconColor.Color
|
||||
local iconTransparency = divideTransparency(iconColor.Transparency, isPressed and 2 or 1)
|
||||
|
||||
return {
|
||||
ButtonIcon = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, 36, 0, 36),
|
||||
Position = UDim2.new(1, -16, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(1, 0.5),
|
||||
Image = Assets.Images.ExpandArrow,
|
||||
ImageColor3 = iconColor3,
|
||||
ImageTransparency = iconTransparency,
|
||||
}),
|
||||
ButtonText = Roact.createElement(ThemedTextLabel, {
|
||||
Text = dropDownOpenText,
|
||||
themeKey = "SecondaryContent",
|
||||
fontKey = "Header2",
|
||||
Position = UDim2.new(0, 12, 0, 0),
|
||||
Size = UDim2.new(1, -12, 1, 0),
|
||||
TextTransparency = transparency,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
}),
|
||||
}
|
||||
end,
|
||||
}),
|
||||
|
||||
CloseDropDownArea = Roact.createElement("TextButton", {
|
||||
BackgroundTransparency = 1,
|
||||
Text = "",
|
||||
Position = UDim2.new(0, -self.state.absolutePositionX, 0, -self.state.absolutePositionY),
|
||||
Size = UDim2.new(0, self.state.screenSizeX, 0, self.state.screenSizeY),
|
||||
Visible = self.state.isOpen,
|
||||
|
||||
[Roact.Event.Activated] = function()
|
||||
self:setState({
|
||||
isOpen = false,
|
||||
})
|
||||
end,
|
||||
ZIndex = 2,
|
||||
}),
|
||||
|
||||
DropDownShadow = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.Shadow.Image,
|
||||
Position = UDim2.new(0, -shadowSize, 0, -shadowSize),
|
||||
Size = UDim2.new(1, shadowSize * 2, 0, shadowSize * 2 + DROP_DOWN_BORDER_SIZE * 2 + itemSize),
|
||||
ScaleType = Assets.Images.Shadow.ScaleType,
|
||||
SliceCenter = Assets.Images.Shadow.SliceCenter,
|
||||
Visible = self.state.isOpen,
|
||||
ZIndex = 3,
|
||||
}, {
|
||||
DropDownBackground = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.RoundedRect.Image,
|
||||
ImageColor3 = style.Theme.BackgroundUIDefault.Color,
|
||||
ImageTransparency = style.Theme.BackgroundUIDefault.Transparency,
|
||||
ScaleType = Assets.Images.RoundedRect.ScaleType,
|
||||
Size = UDim2.new(1, -(shadowSize * 2), 1, -(shadowSize * 2)),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
SliceCenter = Assets.Images.RoundedRect.SliceCenter,
|
||||
SliceScale = 0.5,
|
||||
}, {
|
||||
DropDownList = Roact.createElement(BarOnTopScrollingFrame, {
|
||||
Position = UDim2.new(0, 0, 0, DROP_DOWN_BORDER_SIZE),
|
||||
Size = UDim2.new(1, 0, 1, -DROP_DOWN_BORDER_SIZE * 2),
|
||||
CanvasSize = UDim2.new(1, 0, 0, #self.props.selections * self.state.dropDownItemHeight),
|
||||
scrollBarOffset = 4,
|
||||
}, dropDownList),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function DropDownSelection:watchLayerCollector(layerCollector)
|
||||
self:setState({
|
||||
screenSizeX = layerCollector.AbsoluteSize.X,
|
||||
screenSizeY = layerCollector.AbsoluteSize.Y,
|
||||
})
|
||||
|
||||
self.sizeConnection = layerCollector:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
|
||||
self:setState({
|
||||
screenSizeX = layerCollector.AbsoluteSize.X,
|
||||
screenSizeY = layerCollector.AbsoluteSize.Y,
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
function DropDownSelection:didMount()
|
||||
local root = self.rootRef.current
|
||||
local layerCollector = root:FindFirstAncestorWhichIsA("LayerCollector")
|
||||
|
||||
if layerCollector == nil then
|
||||
self.ancestryConnection = root.AncestryChanged:Connect(function()
|
||||
layerCollector = root:FindFirstAncestorWhichIsA("LayerCollector")
|
||||
if layerCollector ~= nil then
|
||||
self.ancestryConnection:Disconnect()
|
||||
self.ancestryConnection = nil
|
||||
self:watchLayerCollector(layerCollector)
|
||||
end
|
||||
end)
|
||||
else
|
||||
self:watchLayerCollector(layerCollector)
|
||||
end
|
||||
end
|
||||
|
||||
function DropDownSelection:willUnmount()
|
||||
if self.ancestryConnection then
|
||||
self.ancestryConnection:Disconnect()
|
||||
end
|
||||
if self.sizeConnection then
|
||||
self.sizeConnection:Disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
return DropDownSelection
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Cryo = InGameMenuDependencies.Cryo
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local DropDownSelection = require(script.Parent.DropDownSelection)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local dummyDropDownProps = {
|
||||
Size = UDim2.new(0, 200, 0, 44),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
LayoutOrder = 0,
|
||||
|
||||
placeHolderText = "Placeholder",
|
||||
selectedIndex = 0,
|
||||
selections = {
|
||||
"Option1",
|
||||
"Option2",
|
||||
"Option3",
|
||||
},
|
||||
localize = false,
|
||||
enabled = true,
|
||||
selectionChanged = function()
|
||||
print("selection changed")
|
||||
end,
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
DropDownSelection = Roact.createElement(DropDownSelection, dummyDropDownProps),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should create and destroy without errors with a selected item", function()
|
||||
local element = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
DropDownSelection = Roact.createElement(DropDownSelection, Cryo.Dictionary.join(dummyDropDownProps, {
|
||||
selectedIndex = 1,
|
||||
})),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should throw when passed a selectedIndex out of bounds when propvalidation is turned on", function()
|
||||
Roact.setGlobalConfig({
|
||||
propValidation = true,
|
||||
})
|
||||
|
||||
local element = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
DropDownSelection = Roact.createElement(DropDownSelection, Cryo.Dictionary.join(dummyDropDownProps, {
|
||||
selectedIndex = 100,
|
||||
})),
|
||||
}),
|
||||
})
|
||||
|
||||
expect(function()
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end).to.throw()
|
||||
end)
|
||||
end
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RbxAnalyticsService = game:GetService("RbxAnalyticsService")
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local Images = UIBlox.App.ImageSet.Images
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
local CloseNativeClosePrompt = require(InGameMenu.Actions.CloseNativeClosePrompt)
|
||||
local SetMenuIconTooltipOpen = require(InGameMenu.Actions.SetMenuIconTooltipOpen)
|
||||
local OpenMenu = require(InGameMenu.Thunks.OpenMenu)
|
||||
|
||||
local EducationalPopupDialog = require(script.Parent.EducationalPopupDialog)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GetFFlagInGameMenuIconTooltip = require(RobloxGui.Modules.Flags.GetFFlagInGameMenuIconTooltip)
|
||||
|
||||
local NotificationType = GuiService:GetNotificationTypeList()
|
||||
|
||||
local EducationalAnalytics = {
|
||||
EventContext = "educational_popup",
|
||||
ConfirmName = "educational_confirmed",
|
||||
CancelName = "educational_close_app",
|
||||
DismissName = "educational_dismiss_prompt",
|
||||
}
|
||||
|
||||
local EducationalPopup = Roact.PureComponent:extend("EducationalPopup")
|
||||
|
||||
EducationalPopup.validateProps = t.strictInterface({
|
||||
screenSize = t.Vector2,
|
||||
onDismiss = t.callback,
|
||||
onConfirm = t.callback,
|
||||
isClosingApp = t.boolean,
|
||||
})
|
||||
|
||||
function EducationalPopup:init()
|
||||
self.onCancel = function()
|
||||
SendAnalytics(EducationalAnalytics.EventContext, EducationalAnalytics.CancelName, {})
|
||||
GuiService:BroadcastNotification("", NotificationType.NATIVE_EXIT)
|
||||
end
|
||||
end
|
||||
|
||||
function EducationalPopup:render()
|
||||
return withLocalization({
|
||||
bodyTextLn1 = "CoreScripts.InGameMenu.EducationalPopup.BodyTextOpenSystemMenu",
|
||||
bodyTextLn2 = "CoreScripts.InGameMenu.EducationalPopup.BodyTextClickHome",
|
||||
bodyTextLn3 = "CoreScripts.InGameMenu.EducationalPopup.BodyTextPlayMore",
|
||||
titleText = "CoreScripts.InGameMenu.EducationalPopup.Title",
|
||||
confirmText = "CoreScripts.InGameMenu.Ok",
|
||||
cancelText = "CoreScripts.InGameMenu.EducationalPopup.LeaveRoblox",
|
||||
})(function(localized)
|
||||
return Roact.createElement(EducationalPopupDialog, {
|
||||
bodyContents = {
|
||||
{
|
||||
icon = Images["icons/logo/block"],
|
||||
text = localized.bodyTextLn1,
|
||||
layoutOrder = 1,
|
||||
isSystemMenuIcon = true,
|
||||
},
|
||||
{
|
||||
icon = Images["icons/menu/home_off"],
|
||||
text = localized.bodyTextLn2,
|
||||
layoutOrder = 2,
|
||||
},
|
||||
{
|
||||
icon = Images["icons/menu/games_off"],
|
||||
text = localized.bodyTextLn3,
|
||||
layoutOrder = 3,
|
||||
},
|
||||
},
|
||||
cancelText = localized.cancelText,
|
||||
confirmText = localized.confirmText,
|
||||
titleText = localized.titleText,
|
||||
titleBackgroundImageProps = {
|
||||
image = "rbxasset://textures/ui/LuaApp/graphic/EducationalBackground.png",
|
||||
imageHeight = 261,
|
||||
},
|
||||
screenSize = self.props.screenSize,
|
||||
|
||||
onDismiss = self.props.onDismiss,
|
||||
onCancel = self.onCancel,
|
||||
onConfirm = self.props.onConfirm,
|
||||
|
||||
blurBackground = true,
|
||||
visible = self.props.isClosingApp,
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(function(state, props)
|
||||
return {
|
||||
isClosingApp = state.nativeClosePrompt.closingApp,
|
||||
screenSize = state.screenSize,
|
||||
}
|
||||
end, function(dispatch)
|
||||
return {
|
||||
onDismiss = function()
|
||||
dispatch(CloseNativeClosePrompt())
|
||||
RbxAnalyticsService:ReportCounter("EducationalPopup_Dismiss", 1)
|
||||
SendAnalytics(EducationalAnalytics.EventContext, EducationalAnalytics.DismissName, {})
|
||||
end,
|
||||
onConfirm = function()
|
||||
dispatch(CloseNativeClosePrompt())
|
||||
|
||||
if GetFFlagInGameMenuIconTooltip() then
|
||||
dispatch(SetMenuIconTooltipOpen(true))
|
||||
else
|
||||
dispatch(OpenMenu(Constants.AnalyticsMenuOpenTypes.EducationalMenuTriggered))
|
||||
end
|
||||
|
||||
RbxAnalyticsService:ReportCounter("EducationalPopup_Confirm", 1)
|
||||
SendAnalytics(EducationalAnalytics.EventContext, EducationalAnalytics.ConfirmName, {})
|
||||
end,
|
||||
}
|
||||
end)(EducationalPopup)
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local EducationalPopup = require(script.Parent.EducationalPopup)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
EducationalPopup = Roact.createElement(EducationalPopup, {
|
||||
screenSize = Vector2.new(1920, 1080),
|
||||
onDismiss = function()end,
|
||||
onConfirm = function()end,
|
||||
isClosingApp = false,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RunService = game:GetService("RunService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local EducationalModal = UIBlox.App.Dialog.Modal.EducationalModal
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local EducationalPopupDialog = Roact.PureComponent:extend("EducationalPopupDialog")
|
||||
|
||||
EducationalPopupDialog.validateProps = t.strictInterface({
|
||||
bodyContents = t.array(t.strictInterface({
|
||||
icon = t.union(t.string, t.table),
|
||||
text = t.string,
|
||||
layoutOrder = t.integer,
|
||||
isSystemMenuIcon = t.optional(t.boolean),
|
||||
})),
|
||||
cancelText = t.string,
|
||||
confirmText = t.string,
|
||||
titleText = t.string,
|
||||
titleBackgroundImageProps = t.strictInterface({
|
||||
image = t.string,
|
||||
imageHeight = t.number,
|
||||
}),
|
||||
screenSize = t.Vector2,
|
||||
|
||||
onDismiss = t.callback,
|
||||
onCancel = t.callback,
|
||||
onConfirm = t.callback,
|
||||
|
||||
blurBackground = t.boolean,
|
||||
visible = t.boolean,
|
||||
})
|
||||
|
||||
EducationalPopupDialog.defaultProps = {
|
||||
blurBackground = false,
|
||||
}
|
||||
|
||||
function EducationalPopupDialog:render()
|
||||
return withStyle(function(style)
|
||||
return Roact.createElement(Roact.Portal, {
|
||||
target = CoreGui,
|
||||
}, {
|
||||
InGameMenuEducationalPopupDialog = Roact.createElement("ScreenGui", {
|
||||
IgnoreGuiInset = true,
|
||||
OnTopOfCoreBlur = self.props.blurBackground,
|
||||
Enabled = self.props.visible,
|
||||
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||
}, {
|
||||
Overlay = Roact.createElement("TextButton", {
|
||||
AutoButtonColor = false,
|
||||
BackgroundColor3 = style.Theme.Overlay.Color,
|
||||
BackgroundTransparency = style.Theme.Overlay.Transparency,
|
||||
BorderSizePixel = 0,
|
||||
Size = UDim2.fromScale(1, 1),
|
||||
Text = "",
|
||||
}),
|
||||
Popup = Roact.createElement(EducationalModal, {
|
||||
bodyContents = self.props.bodyContents,
|
||||
cancelText = self.props.cancelText,
|
||||
confirmText = self.props.confirmText,
|
||||
titleText = self.props.titleText,
|
||||
titleBackgroundImageProps = self.props.titleBackgroundImageProps,
|
||||
screenSize = self.props.screenSize,
|
||||
onDismiss = self.props.onDismiss,
|
||||
onCancel = self.props.onCancel,
|
||||
onConfirm = self.props.onConfirm,
|
||||
})
|
||||
})
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
function EducationalPopupDialog:updateBlur()
|
||||
local shouldBlur = self.props.blurBackground and self.props.visible
|
||||
RunService:SetRobloxGuiFocused(shouldBlur)
|
||||
end
|
||||
|
||||
function EducationalPopupDialog:didMount()
|
||||
self:updateBlur()
|
||||
end
|
||||
|
||||
function EducationalPopupDialog:didUpdate()
|
||||
self:updateBlur()
|
||||
end
|
||||
|
||||
function EducationalPopupDialog:willUnmount()
|
||||
if self.props.blurBackground then
|
||||
RunService:SetRobloxGuiFocused(false)
|
||||
end
|
||||
end
|
||||
|
||||
return EducationalPopupDialog
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local Images = UIBlox.App.ImageSet.Images
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local EducationalPopupDialog = require(script.Parent.EducationalPopupDialog)
|
||||
|
||||
local mockProps = {
|
||||
bodyContents = {
|
||||
{
|
||||
icon = Images["icons/logo/block"],
|
||||
text = "Body 1",
|
||||
layoutOrder = 1
|
||||
},
|
||||
{
|
||||
icon = Images["icons/menu/home_on"],
|
||||
text = "Body 2",
|
||||
layoutOrder = 2
|
||||
},
|
||||
{
|
||||
icon = Images["icons/menu/games_on"],
|
||||
text = "Body 3",
|
||||
layoutOrder = 3
|
||||
},
|
||||
},
|
||||
cancelText = "Cancel",
|
||||
confirmText = "Confirm",
|
||||
titleText = "Title",
|
||||
titleBackgroundImageProps = {
|
||||
image = "rbxassetid://2610133241",
|
||||
imageHeight = 200,
|
||||
},
|
||||
screenSize = Vector2.new(1920, 1080),
|
||||
|
||||
onDismiss = function()end,
|
||||
onCancel = function()end,
|
||||
onConfirm = function()end,
|
||||
|
||||
blurBackground = true,
|
||||
visible = true,
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
EducationalPopupDialog = Roact.createElement(EducationalPopupDialog, mockProps),
|
||||
}),
|
||||
})
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local AppStorageService = game:GetService("AppStorageService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local FullscreenTitleBarComponent = UIBlox.App.Bar.FullscreenTitleBar
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local InGameMenuPolicy = require(InGameMenu.InGameMenuPolicy)
|
||||
|
||||
local OpenEducationalPopup = require(InGameMenu.Thunks.OpenEducationalPopup)
|
||||
local StartLeavingGame = require(InGameMenu.Actions.StartLeavingGame)
|
||||
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local UserLocalStore = require(InGameMenu.Utility.UserLocalStore)
|
||||
|
||||
local GetFFlagPlayerSpecificPopupCounter = require(InGameMenu.Flags.GetFFlagPlayerSpecificPopupCounter)
|
||||
|
||||
local DISAPPEAR_DELAY = 0.5
|
||||
|
||||
local FullscreenTitleBar = Roact.PureComponent:extend("FullscreenTitleBar")
|
||||
|
||||
FullscreenTitleBar.validateProps = t.strictInterface({
|
||||
titleText = t.optional(t.string),
|
||||
displayOrder = t.integer,
|
||||
isMenuOpen = t.boolean,
|
||||
isEducationalPopupEnabled = t.boolean,
|
||||
maxDisplayCount = t.integer,
|
||||
openEducationalPopup = t.callback,
|
||||
startLeavingGame = t.callback,
|
||||
})
|
||||
|
||||
FullscreenTitleBar.defaultProps = {
|
||||
titleText = "Roblox",
|
||||
}
|
||||
|
||||
function FullscreenTitleBar:init()
|
||||
self:setState({
|
||||
isTriggered = false,
|
||||
fullscreenEnabled = UserGameSettings:InFullScreen(),
|
||||
})
|
||||
|
||||
self.onFullscreenChanged = function(newFullscreenStatus)
|
||||
self:setState({
|
||||
fullscreenEnabled = newFullscreenStatus,
|
||||
isTriggered = false,
|
||||
})
|
||||
end
|
||||
|
||||
self.triggerTitleBar = function()
|
||||
if not self.state.isTriggered then
|
||||
self:setState({
|
||||
isTriggered = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
self.hideTitleBar = function()
|
||||
if self.state.isTriggered then
|
||||
self:setState({
|
||||
isTriggered = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
self.onMouseLeave = function()
|
||||
delay(DISAPPEAR_DELAY, self.hideTitleBar)
|
||||
end
|
||||
|
||||
self.exitFullscreen = function()
|
||||
self.hideTitleBar()
|
||||
GuiService:ToggleFullscreen()
|
||||
end
|
||||
|
||||
self.closeRoblox = function()
|
||||
self.hideTitleBar()
|
||||
|
||||
if self.props.isEducationalPopupEnabled then
|
||||
local localStore = AppStorageService
|
||||
if GetFFlagPlayerSpecificPopupCounter() then
|
||||
localStore = UserLocalStore.new()
|
||||
end
|
||||
self.props.openEducationalPopup(GuiService, localStore, self.props.maxDisplayCount)
|
||||
else
|
||||
self.props.startLeavingGame()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function FullscreenTitleBar:render()
|
||||
return Roact.createFragment({
|
||||
FullscreenChangedEvent = Roact.createElement(ExternalEventConnection, {
|
||||
event = UserGameSettings.FullscreenChanged,
|
||||
callback = self.onFullscreenChanged,
|
||||
}),
|
||||
FullscreenTitleBar = Roact.createElement(Roact.Portal, {
|
||||
target = CoreGui,
|
||||
}, {
|
||||
InGameFullscreenTitleBarScreen = Roact.createElement("ScreenGui", {
|
||||
Enabled = self.state.fullscreenEnabled,
|
||||
IgnoreGuiInset = true,
|
||||
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||
DisplayOrder = self.props.displayOrder,
|
||||
}, {
|
||||
TriggerArea = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
Size = UDim2.new(1, 0, 0, 1),
|
||||
[Roact.Event.MouseEnter] = self.triggerTitleBar,
|
||||
}),
|
||||
Bar = Roact.createElement(FullscreenTitleBarComponent, {
|
||||
title = self.props.titleText,
|
||||
isTriggered = self.state.isTriggered,
|
||||
onDisappear = self.onMouseLeave,
|
||||
exitFullscreen = self.exitFullscreen,
|
||||
closeRoblox = self.closeRoblox,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
function FullscreenTitleBar:didUpdate(prevProps)
|
||||
if not prevProps.isMenuOpen and self.props.isMenuOpen then
|
||||
self.hideTitleBar()
|
||||
end
|
||||
end
|
||||
|
||||
FullscreenTitleBar = InGameMenuPolicy.connect(function(appPolicy, props)
|
||||
return {
|
||||
isEducationalPopupEnabled = appPolicy.enableEducationalPopup(),
|
||||
maxDisplayCount = appPolicy.educationalPopupMaxDisplayCount(),
|
||||
}
|
||||
end)(FullscreenTitleBar)
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
isMenuOpen = state.isMenuOpen,
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
openEducationalPopup = function(guiService, appStorageService, maxCount)
|
||||
return dispatch(OpenEducationalPopup(guiService, appStorageService, maxCount))
|
||||
end,
|
||||
startLeavingGame = function()
|
||||
dispatch(StartLeavingGame())
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(FullscreenTitleBar)
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
local InGameMenuPolicy = require(InGameMenu.InGameMenuPolicy)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local mockPolicyMapper = function(policy)
|
||||
return {
|
||||
enableEducationalPopup = function()
|
||||
return false
|
||||
end,
|
||||
educationalPopupMaxDisplayCount = function()
|
||||
return 0
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
local FullscreenTitleBar = require(script.Parent.FullscreenTitleBar)
|
||||
|
||||
local mockProps = {
|
||||
titleText = "Roblox",
|
||||
displayOrder = 1,
|
||||
isMenuOpen = true,
|
||||
isEducationalPopupEnabled = true,
|
||||
maxDisplayCount = 1,
|
||||
openEducationalPopup = function() end,
|
||||
startLeavingGame = function() end,
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
PolicyProvider = Roact.createElement(InGameMenuPolicy.Provider, {
|
||||
policy = { mockPolicyMapper },
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
FullscreenTitleBar = Roact.createElement(FullscreenTitleBar, mockProps),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
local handle = Roact.mount(element)
|
||||
Roact.unmount(handle)
|
||||
end)
|
||||
|
||||
it("should be a child ScreenGui of CoreGui", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
PolicyProvider = Roact.createElement(InGameMenuPolicy.Provider, {
|
||||
policy = { mockPolicyMapper },
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
FullscreenTitleBar = Roact.createElement(FullscreenTitleBar, mockProps),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local handle = Roact.mount(element)
|
||||
local instance = CoreGui:FindFirstChild("InGameFullscreenTitleBarScreen")
|
||||
expect(instance).to.never.equal(nil)
|
||||
expect(instance.ClassName).to.equal("ScreenGui")
|
||||
Roact.unmount(handle)
|
||||
end)
|
||||
end
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
--[[
|
||||
A square game icon
|
||||
]]
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local t = InGameMenuDependencies.t
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
local Images = UIBlox.App.ImageSet.Images
|
||||
local AssetImage = require(InGameMenu.Components.AssetImage)
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local getGameIconRequestSize = require(InGameMenu.Components.getGameIconRequestSize)
|
||||
|
||||
local MAX_PLACEHOLDER_SIZE = 44
|
||||
|
||||
local GameIcon = Roact.PureComponent:extend("GameIcon")
|
||||
|
||||
GameIcon.validateProps = t.strictInterface({
|
||||
gameId = t.number,
|
||||
iconSize = t.numberMin(0),
|
||||
layoutOrder = t.optional(t.number),
|
||||
})
|
||||
|
||||
function GameIcon:render()
|
||||
local iconSize = self.props.iconSize
|
||||
local requestIconSize = getGameIconRequestSize(iconSize)
|
||||
local gameId = self.props.gameId
|
||||
|
||||
local iconUrl = ("rbxthumb://type=GameIcon&id=%d&w=%d&h=%d"):format(
|
||||
gameId,
|
||||
requestIconSize,
|
||||
requestIconSize
|
||||
)
|
||||
|
||||
--Special case for when the gameId is not available. For example developing in studio with a un-published game.
|
||||
local isPlaceholder = gameId == 0
|
||||
|
||||
if isPlaceholder then
|
||||
local placeholderIconSize = MAX_PLACEHOLDER_SIZE
|
||||
if iconSize <= MAX_PLACEHOLDER_SIZE then
|
||||
placeholderIconSize = iconSize
|
||||
end
|
||||
return withStyle(function(style)
|
||||
return Roact.createElement(AssetImage.Label, {
|
||||
imageKey = "RoundedRect",
|
||||
Size = UDim2.fromOffset(iconSize, iconSize),
|
||||
ImageColor3 = style.Theme.PlaceHolder.Color,
|
||||
ImageTransparency = style.Theme.PlaceHolder.Transparency,
|
||||
LayoutOrder = self.props.layoutOrder,
|
||||
}, {
|
||||
PlaceholderIcon = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.fromOffset(placeholderIconSize, placeholderIconSize),
|
||||
Position = UDim2.fromScale(0.5, 0.5),
|
||||
Image = Images["icons/imageUnavailable"],
|
||||
ImageColor3 = style.Theme.UIDefault.Color,
|
||||
ImageTransparency = style.Theme.UIDefault.Transparency,
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
}),
|
||||
})
|
||||
end)
|
||||
else
|
||||
return Roact.createElement("ImageLabel", {
|
||||
Image = iconUrl,
|
||||
Size = UDim2.fromOffset(iconSize, iconSize),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = self.props.layoutOrder,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return GameIcon
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local GameIcon = require(script.Parent.GameIcon)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
GameIcon = Roact.createElement(GameIcon, {
|
||||
gameId = game.gameId,
|
||||
iconSize = 100,
|
||||
layoutOrder = 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should create and destroy placeholder game icon without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
GameIcon = Roact.createElement(GameIcon, {
|
||||
gameId = 0,
|
||||
iconSize = 100,
|
||||
layoutOrder = 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
local GameIcon = require(InGameMenu.Components.GameIcon)
|
||||
|
||||
local GAME_ICON_RENDER_SIZE = 100
|
||||
|
||||
local EXTERIOR_PADDING = 24
|
||||
local TEXT_ICON_PADDING = 12
|
||||
|
||||
local function GameIconHeader(props)
|
||||
return Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, GAME_ICON_RENDER_SIZE + EXTERIOR_PADDING * 2),
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingBottom = UDim.new(0, EXTERIOR_PADDING),
|
||||
PaddingLeft = UDim.new(0, EXTERIOR_PADDING),
|
||||
PaddingRight = UDim.new(0, EXTERIOR_PADDING),
|
||||
PaddingTop = UDim.new(0, EXTERIOR_PADDING),
|
||||
}),
|
||||
GameIcon = Roact.createElement(GameIcon, {
|
||||
gameId = game.GameId,
|
||||
iconSize = 100,
|
||||
}),
|
||||
GameName = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Title",
|
||||
themeKey = "TextEmphasis",
|
||||
|
||||
Position = UDim2.new(0, GAME_ICON_RENDER_SIZE + TEXT_ICON_PADDING, 0, 0),
|
||||
Size = UDim2.new(1, -(GAME_ICON_RENDER_SIZE + TEXT_ICON_PADDING), 1, 0),
|
||||
Text = props.gameName,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
TextWrapped = true,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(function(state)
|
||||
return {
|
||||
gameName = state.localization.currentGameName,
|
||||
}
|
||||
end)(GameIconHeader)
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local GameIconHeader = require(script.Parent.GameIconHeader)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
GameIconHeader = Roact.createElement(GameIconHeader),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local MicroProfilerChanged = UserGameSettings:GetPropertyChangedSignal("OnScreenProfilerEnabled")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local DevConsoleMaster = require(CoreGui.RobloxGui.Modules.DevConsoleMaster)
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local Divider = require(InGameMenu.Components.Divider)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local Page = require(script.Parent.Parent.Page)
|
||||
|
||||
local CategoryHeader = require(script.Parent.CategoryHeader)
|
||||
local ToggleEntry = require(script.Parent.ToggleEntry)
|
||||
local VersionReporter = require(script.Parent.VersionReporter)
|
||||
|
||||
local CloseMenu = require(InGameMenu.Thunks.CloseMenu)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local AdvancedPage = Roact.PureComponent:extend("AdvancedPage")
|
||||
|
||||
AdvancedPage.validateProps = t.strictInterface({
|
||||
closeMenu = t.callback,
|
||||
pageTitle = t.string,
|
||||
})
|
||||
|
||||
function AdvancedPage:init()
|
||||
self:setState({
|
||||
microProfilerEnabled = UserGameSettings.OnScreenProfilerEnabled,
|
||||
performanceStatsEnabled = UserGameSettings.PerformanceStatsVisible,
|
||||
})
|
||||
end
|
||||
|
||||
function AdvancedPage:render()
|
||||
return withStyle(function(style)
|
||||
return Roact.createElement(Page, {
|
||||
pageTitle = self.props.pageTitle,
|
||||
zIndex = 2,
|
||||
position = self.props.position,
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
}),
|
||||
AdvancedHeader = Roact.createElement(CategoryHeader, {
|
||||
LayoutOrder = 1,
|
||||
localizationKey = "CoreScripts.InGameMenu.GameSettings.AdvancedSettingsTitle",
|
||||
}),
|
||||
PerformanceStats = Roact.createElement(ToggleEntry, {
|
||||
LayoutOrder = 2,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.ShowPerfStats",
|
||||
checked = self.state.performanceStatsEnabled,
|
||||
onToggled = function()
|
||||
UserGameSettings.PerformanceStatsVisible = not UserGameSettings.PerformanceStatsVisible
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
}),
|
||||
MicroProfiler = Roact.createElement(ToggleEntry, {
|
||||
LayoutOrder = 3,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.ShowMicroProfiler",
|
||||
checked = self.state.microProfilerEnabled,
|
||||
onToggled = function()
|
||||
UserGameSettings.OnScreenProfilerEnabled = not UserGameSettings.OnScreenProfilerEnabled
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
}),
|
||||
DeveloperConsole = withLocalization({
|
||||
text = "CoreScripts.InGameMenu.GameSettings.DeveloperConsole"
|
||||
})(function(localized)
|
||||
return Roact.createElement("TextButton", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, 54),
|
||||
Text = localized.text,
|
||||
TextColor3 = style.Theme.TextEmphasis.Color,
|
||||
Font = style.Font.Header2.Font,
|
||||
TextSize = style.Font.Header2.RelativeSize * style.Font.BaseSize,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
LayoutOrder = 4,
|
||||
[Roact.Event.Activated] = function()
|
||||
DevConsoleMaster:SetVisibility(true)
|
||||
self.props.closeMenu()
|
||||
end,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, 24),
|
||||
}),
|
||||
})
|
||||
end),
|
||||
Divider = Roact.createElement(Divider, {
|
||||
Size = UDim2.new(1, -24, 0, 1),
|
||||
LayoutOrder = 5,
|
||||
}),
|
||||
VersionReporter = Roact.createElement(VersionReporter, {
|
||||
LayoutOrder = 6,
|
||||
}),
|
||||
|
||||
MicroProfilerVisibilityListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = MicroProfilerChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
microProfilerEnabled = UserGameSettings.OnScreenProfilerEnabled,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
PerformanceStatsVisibilityListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = UserGameSettings.PerformanceStatsVisibleChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
performanceStatsEnabled = UserGameSettings.PerformanceStatsVisible,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(nil, function(dispatch)
|
||||
return {
|
||||
closeMenu = function()
|
||||
dispatch(CloseMenu)
|
||||
end
|
||||
}
|
||||
end)(AdvancedPage)
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
|
||||
local ToggleEntry = require(script.Parent.ToggleEntry)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local AutoPropertyToggleEntry = Roact.PureComponent:extend("AutoPropertyToggleEntry")
|
||||
AutoPropertyToggleEntry.validateProps = t.strictInterface({
|
||||
instance = t.Instance,
|
||||
key = t.string,
|
||||
lockedToOff = t.optional(t.boolean),
|
||||
onValue = t.optional(t.any),
|
||||
offValue = t.optional(t.any),
|
||||
labelKey = t.string,
|
||||
LayoutOrder = t.integer,
|
||||
subtextEnabled = t.optional(t.boolean),
|
||||
subtextKey = t.optional(t.string),
|
||||
})
|
||||
|
||||
AutoPropertyToggleEntry.defaultProps = {
|
||||
onValue = true,
|
||||
offValue = false,
|
||||
lockedToOff = false,
|
||||
subtextEnabled = false,
|
||||
subtextKey = "",
|
||||
}
|
||||
|
||||
function AutoPropertyToggleEntry:init(props)
|
||||
local currentValue = props.instance[props.key]
|
||||
self.changeEvent = props.instance:GetPropertyChangedSignal(props.key)
|
||||
|
||||
self:setState({
|
||||
checked = not props.lockedToOff and currentValue == props.onValue,
|
||||
})
|
||||
|
||||
self.onExternalChange = function()
|
||||
local newValue = props.instance[props.key]
|
||||
self:setState({
|
||||
checked = newValue == props.onValue
|
||||
})
|
||||
end
|
||||
|
||||
self.onToggled = function()
|
||||
if self.props.lockedToOff then
|
||||
return
|
||||
end
|
||||
|
||||
local isOn = props.instance[props.key] == props.onValue
|
||||
|
||||
if isOn then
|
||||
props.instance[props.key] = props.offValue
|
||||
else
|
||||
props.instance[props.key] = props.onValue
|
||||
end
|
||||
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end
|
||||
end
|
||||
|
||||
function AutoPropertyToggleEntry:render()
|
||||
local props = self.props
|
||||
|
||||
return Roact.createFragment({
|
||||
Toggle = Roact.createElement(ToggleEntry, {
|
||||
checked = self.state.checked,
|
||||
lockedToOff = props.lockedToOff,
|
||||
labelKey = props.labelKey,
|
||||
LayoutOrder = props.LayoutOrder,
|
||||
onToggled = self.onToggled,
|
||||
subtextEnabled = props.subtextEnabled,
|
||||
subtextKey = props.subtextKey,
|
||||
}),
|
||||
|
||||
Listener = Roact.createElement(ExternalEventConnection, {
|
||||
event = self.changeEvent,
|
||||
callback = self.onExternalChange,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return AutoPropertyToggleEntry
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local AutoPropertyToggleEntry = require(script.Parent.AutoPropertyToggleEntry)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local dummyValue = Instance.new("BoolValue")
|
||||
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
AutoPropertyToggleEntry = Roact.createElement(AutoPropertyToggleEntry, {
|
||||
instance = dummyValue,
|
||||
key = "Value",
|
||||
lockedToOff = false,
|
||||
onValue = true,
|
||||
offValue = false,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.ShowPerfStats",
|
||||
LayoutOrder = 2,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
|
||||
dummyValue.Value = false
|
||||
dummyValue.Value = true
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
local Players = game:GetService("Players")
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local VRService = game:GetService("VRService")
|
||||
local RobloxGui = game:GetService("CoreGui"):WaitForChild("RobloxGui")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local localPlayer = Players.LocalPlayer
|
||||
local DevEnableMouseLockChanged = localPlayer:GetPropertyChangedSignal("DevEnableMouseLock")
|
||||
|
||||
local IsUsingCameraYInvertedChanged = UserGameSettings:GetPropertyChangedSignal("IsUsingCameraYInverted")
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local Divider = require(InGameMenu.Components.Divider)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local Page = require(InGameMenu.Components.Page)
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
|
||||
local SetCurrentPage = require(InGameMenu.Actions.SetCurrentPage)
|
||||
|
||||
local AutoPropertyToggleEntry = require(script.Parent.AutoPropertyToggleEntry)
|
||||
local CameraModeEntry = require(script.Parent.CameraModeEntry)
|
||||
local CameraSensitivityEntry = require(script.Parent.CameraSensitivityEntry)
|
||||
local CategoryHeader = require(script.Parent.CategoryHeader)
|
||||
local GraphicsQualityEntry = require(script.Parent.GraphicsQualityEntry)
|
||||
local MovementModeEntry = require(script.Parent.MovementModeEntry)
|
||||
local ToggleEntry = require(script.Parent.ToggleEntry)
|
||||
local VolumeEntry = require(script.Parent.VolumeEntry)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local FFlagInGameMenuVRToggle = game:DefineFastFlag("InGameMenuVRToggle", false)
|
||||
|
||||
local GetFFlagFullscreenAnalytics = require(RobloxGui.Modules.Flags.GetFFlagFullscreenAnalytics)
|
||||
|
||||
local VRAvailableChanged = VRService:GetPropertyChangedSignal("VREnabled")
|
||||
local VREnabledChanged = UserGameSettings:GetPropertyChangedSignal("VREnabled")
|
||||
|
||||
local BasicPage = Roact.PureComponent:extend("BasicPage")
|
||||
BasicPage.validateProps = t.strictInterface({
|
||||
switchToAdvancedPage = t.callback,
|
||||
pageTitle = t.string,
|
||||
})
|
||||
|
||||
local vrEnabledAtModuleLoad = UserGameSettings.VREnabled
|
||||
|
||||
function BasicPage:init()
|
||||
self:setState({
|
||||
shiftLockEnabled = localPlayer.DevEnableMouseLock,
|
||||
fullScreenEnabled = UserGameSettings:InFullScreen(),
|
||||
invertedCameraEnabled = UserGameSettings.IsUsingCameraYInverted,
|
||||
vrAvailable = VRService.VREnabled,
|
||||
vrEnabled = UserGameSettings.VREnabled,
|
||||
})
|
||||
|
||||
self.pageSize, self.setPageSize = Roact.createBinding(UDim2.new(0, 0, 0, 0))
|
||||
end
|
||||
|
||||
function BasicPage:render()
|
||||
local shouldSettingsDisabledInVRBeShown = not (FFlagInGameMenuVRToggle and self.state.vrEnabled and self.state.vrAvailable)
|
||||
|
||||
return Roact.createElement(Page, {
|
||||
pageTitle = self.props.pageTitle,
|
||||
position = self.props.position,
|
||||
}, {
|
||||
PageContents = Roact.createElement("ScrollingFrame", {
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
CanvasSize = self.pageSize,
|
||||
Position = self.props.position,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
[Roact.Change.AbsoluteContentSize] = function(rbx)
|
||||
self.setPageSize(UDim2.new(0, 0, 0, rbx.AbsoluteContentSize.Y))
|
||||
end,
|
||||
}),
|
||||
|
||||
CameraHeader = Roact.createElement(CategoryHeader, {
|
||||
LayoutOrder = 1,
|
||||
localizationKey = "CoreScripts.InGameMenu.GameSettings.CameraTitle",
|
||||
}),
|
||||
CameraMode = shouldSettingsDisabledInVRBeShown and Roact.createElement(CameraModeEntry, {
|
||||
LayoutOrder = 2,
|
||||
}),
|
||||
InvertedCamera = shouldSettingsDisabledInVRBeShown and self.state.invertedCameraEnabled and Roact.createElement(AutoPropertyToggleEntry, {
|
||||
LayoutOrder = 3,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.InvertedCamera",
|
||||
instance = UserGameSettings,
|
||||
key = "CameraYInverted",
|
||||
}),
|
||||
CameraSensitivity = shouldSettingsDisabledInVRBeShown and Roact.createElement(CameraSensitivityEntry, {
|
||||
LayoutOrder = 4,
|
||||
}),
|
||||
CameraDivider = Roact.createElement(Divider, {
|
||||
LayoutOrder = 5,
|
||||
Size = UDim2.new(1, -24, 0, 1),
|
||||
}),
|
||||
|
||||
ControlsHeader = Roact.createElement(CategoryHeader, {
|
||||
LayoutOrder = 6,
|
||||
localizationKey = "CoreScripts.InGameMenu.GameSettings.ControlsAudio",
|
||||
}),
|
||||
ShiftLock = shouldSettingsDisabledInVRBeShown and Roact.createElement(AutoPropertyToggleEntry, {
|
||||
LayoutOrder = 7,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.ShiftLock",
|
||||
instance = UserGameSettings,
|
||||
key = "ControlMode",
|
||||
onValue = Enum.ControlMode.MouseLockSwitch,
|
||||
offValue = Enum.ControlMode.Classic,
|
||||
lockedToOff = not self.state.shiftLockEnabled,
|
||||
}),
|
||||
MovementMode = shouldSettingsDisabledInVRBeShown and Roact.createElement(MovementModeEntry, {
|
||||
LayoutOrder = 8,
|
||||
}),
|
||||
VolumeEntry = Roact.createElement(VolumeEntry, {
|
||||
LayoutOrder = 9,
|
||||
}),
|
||||
ControlsDivider = Roact.createElement(Divider, {
|
||||
LayoutOrder = 10,
|
||||
Size = UDim2.new(1, -24, 0, 1),
|
||||
}),
|
||||
|
||||
GraphicsHeader = Roact.createElement(CategoryHeader, {
|
||||
LayoutOrder = 11,
|
||||
localizationKey = "CoreScripts.InGameMenu.GameSettings.GraphicsTitle",
|
||||
}),
|
||||
GraphicsQualityEntry = Roact.createElement(GraphicsQualityEntry, {
|
||||
LayoutOrder = 12,
|
||||
}),
|
||||
FullScreen = shouldSettingsDisabledInVRBeShown and Roact.createElement(ToggleEntry, {
|
||||
LayoutOrder = 13,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.FullScreen",
|
||||
checked = self.state.fullScreenEnabled,
|
||||
onToggled = function()
|
||||
GuiService:ToggleFullscreen()
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
}),
|
||||
VRMode = FFlagInGameMenuVRToggle and self.state.vrAvailable and Roact.createElement(AutoPropertyToggleEntry, {
|
||||
LayoutOrder = 14,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.VREnabled",
|
||||
instance = UserGameSettings,
|
||||
key = "VREnabled",
|
||||
subtextEnabled = self.state.vrEnabled ~= vrEnabledAtModuleLoad,
|
||||
subtextKey = "CoreScripts.InGameMenu.GameSettings.RestartPending",
|
||||
}),
|
||||
GraphicsDivider = Roact.createElement(Divider, {
|
||||
LayoutOrder = 15,
|
||||
Size = UDim2.new(1, -24, 0, 1),
|
||||
}),
|
||||
AdvancedSettings = Roact.createElement("TextButton", {
|
||||
LayoutOrder = 16,
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, 54),
|
||||
Text = "",
|
||||
[Roact.Event.Activated] = self.props.switchToAdvancedPage,
|
||||
}, {
|
||||
Text = withLocalization({
|
||||
text = "CoreScripts.InGameMenu.GameSettings.SwitchToAdvancedSettings",
|
||||
})(function(localization)
|
||||
return Roact.createElement(ThemedTextLabel, {
|
||||
Text = localization.text,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
fontKey = "Body",
|
||||
themeKey = "TextDefault",
|
||||
Size = UDim2.new(1, -96, 1, 0),
|
||||
Position = UDim2.new(0, 24, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
})
|
||||
end),
|
||||
Icon = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.NavigateForward,
|
||||
Size = UDim2.new(0, 36, 0, 36),
|
||||
Position = UDim2.new(1, -36, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(1, 0.5),
|
||||
}),
|
||||
}),
|
||||
AdvancedDivider = Roact.createElement(Divider, {
|
||||
LayoutOrder = 17,
|
||||
Size = UDim2.new(1, -24, 0, 1),
|
||||
}),
|
||||
|
||||
ShiftLockEnabledListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = DevEnableMouseLockChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
shiftLockEnabled = localPlayer.DevEnableMouseLock,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
FullScreenListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = UserGameSettings.FullscreenChanged,
|
||||
callback = function(newFullscreenStatus)
|
||||
self:setState({
|
||||
fullScreenEnabled = newFullscreenStatus,
|
||||
})
|
||||
|
||||
if GetFFlagFullscreenAnalytics() then
|
||||
SendAnalytics(Constants.AnalyticsInGameMenuName, Constants.AnalyticsFullscreenModeName, {
|
||||
enabled = newFullscreenStatus,
|
||||
})
|
||||
end
|
||||
end,
|
||||
}),
|
||||
CameraYInvertedListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = IsUsingCameraYInvertedChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
invertedCameraEnabled = UserGameSettings.IsUsingCameraYInverted,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
VRAvailableListener = FFlagInGameMenuVRToggle and Roact.createElement(ExternalEventConnection, {
|
||||
event = VRAvailableChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
vrAvailable = VRService.VREnabled
|
||||
})
|
||||
end,
|
||||
}),
|
||||
VREnabledListener = FFlagInGameMenuVRToggle and Roact.createElement(ExternalEventConnection, {
|
||||
event = VREnabledChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
vrEnabled = UserGameSettings.VREnabled
|
||||
})
|
||||
end,
|
||||
}),
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(nil, function(dispatch)
|
||||
return {
|
||||
switchToAdvancedPage = function()
|
||||
dispatch(SetCurrentPage(Constants.advancedSettingsPageKey))
|
||||
end,
|
||||
}
|
||||
end)(BasicPage)
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Players = game:GetService("Players")
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
|
||||
local ComputerCameraMovementModeChanged = UserGameSettings:GetPropertyChangedSignal("ComputerCameraMovementMode")
|
||||
|
||||
local localPlayer = Players.LocalPlayer
|
||||
local playerScripts = localPlayer:WaitForChild("PlayerScripts")
|
||||
local DevComputerMovementModeChanged = localPlayer:GetPropertyChangedSignal("DevComputerMovementMode")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local DropDownSelection = require(InGameMenu.Components.DropDownSelection)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
|
||||
local InputLabel = require(script.Parent.InputLabel)
|
||||
local DeveloperLockLabel = require(script.Parent.DeveloperLockLabel)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local CAMERA_MODE_LOCALIZATION_KEYS = {
|
||||
[Enum.ComputerCameraMovementMode.Classic] = "CoreScripts.InGameMenu.GameSettings.CameraModeClassic",
|
||||
[Enum.ComputerCameraMovementMode.Follow] = "CoreScripts.InGameMenu.GameSettings.CameraModeFollow",
|
||||
[Enum.ComputerCameraMovementMode.Default] = "CoreScripts.InGameMenu.GameSettings.Default",
|
||||
[Enum.ComputerCameraMovementMode.CameraToggle] = "CoreScripts.InGameMenu.GameSettings.CameraModeCameraToggle",
|
||||
}
|
||||
|
||||
local CameraModeEntry = Roact.PureComponent:extend("CameraSensitivityEntry")
|
||||
CameraModeEntry.validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
function CameraModeEntry:init()
|
||||
self:setState({
|
||||
selectedComputerMode = UserGameSettings.ComputerCameraMovementMode,
|
||||
computerOptions = playerScripts:GetRegisteredComputerCameraMovementModes(),
|
||||
developerComputerMode = localPlayer.DevComputerCameraMode,
|
||||
})
|
||||
end
|
||||
|
||||
function CameraModeEntry:render()
|
||||
-- TODO: Support gamepad and touch controls.
|
||||
|
||||
local result = {
|
||||
ComputerCameraMovementModeListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = ComputerCameraMovementModeChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
selectedComputerMode = UserGameSettings.ComputerCameraMovementMode,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
ComputerCameraMovementModeRegisteredListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = playerScripts.ComputerCameraMovementModeRegistered,
|
||||
callback = function()
|
||||
self:setState({
|
||||
computerOptions = playerScripts:GetRegisteredComputerCameraMovementModes(),
|
||||
})
|
||||
end,
|
||||
}),
|
||||
LocalPlayerComputerMovementMode = Roact.createElement(ExternalEventConnection, {
|
||||
event = DevComputerMovementModeChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
developerComputerMode = localPlayer.DevComputerCameraMode,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
}
|
||||
|
||||
if #self.state.computerOptions == 0 then
|
||||
return Roact.createFragment(result)
|
||||
end
|
||||
|
||||
local disabled = self.state.developerComputerMode ~= Enum.DevComputerCameraMovementMode.UserChoice
|
||||
local optionTexts = {}
|
||||
local selectedIndex = 0
|
||||
|
||||
if not disabled then
|
||||
for index, enum in ipairs(self.state.computerOptions) do
|
||||
optionTexts[index] = CAMERA_MODE_LOCALIZATION_KEYS[enum]
|
||||
assert(optionTexts[index] ~= nil, "Camera movement mode " .. enum.Name .. " has no localization key")
|
||||
|
||||
if self.state.selectedComputerMode.Value == enum.Value then
|
||||
selectedIndex = index
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
result.CameraModeEntrySelector = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, 44 + 56 + 20),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = self.props.LayoutOrder,
|
||||
ZIndex = 2,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, 24),
|
||||
PaddingRight = UDim.new(0, 24),
|
||||
}),
|
||||
InputLabel = Roact.createElement(InputLabel, {
|
||||
localizationKey = "CoreScripts.InGameMenu.GameSettings.CameraMode",
|
||||
Size = UDim2.new(1, 0, 0, 56),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
AnchorPoint = Vector2.new(0, 0),
|
||||
}),
|
||||
Dropdown = withLocalization({
|
||||
placeholder = disabled and CAMERA_MODE_LOCALIZATION_KEYS[self.state.developerComputerMode] or nil
|
||||
})(function(localized)
|
||||
return Roact.createElement(DropDownSelection, {
|
||||
Size = UDim2.new(1, 0, 0, 44),
|
||||
Position = UDim2.new(0, 0, 0, 56),
|
||||
selections = optionTexts,
|
||||
selectedIndex = selectedIndex,
|
||||
placeHolderText = self.state.developerComputerMode.Name,
|
||||
enabled = not disabled,
|
||||
localize = true,
|
||||
selectionChanged = function(newSelection)
|
||||
UserGameSettings.ComputerCameraMovementMode = self.state.computerOptions[newSelection]
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
})
|
||||
end),
|
||||
LockedLabel = disabled and Roact.createElement(DeveloperLockLabel, {
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
Position = UDim2.new(0, 0, 1, 0),
|
||||
Size = UDim2.new(1, -72, 0.25, 0),
|
||||
}),
|
||||
})
|
||||
|
||||
return Roact.createFragment(result)
|
||||
end
|
||||
|
||||
return CameraModeEntry
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
local MIN_ENGINE_MOUSE_SENSITIVITY = 0
|
||||
local MAX_ENGINE_MOUSE_SENSITIVITY = 10
|
||||
local MIN_GUI_MOUSE_SENSITIVITY = 0
|
||||
local MAX_GUI_MOUSE_SENSITIVITY = 10
|
||||
local GUI_MOUSE_SENSITIVITY_GRANULARITY = 0.01
|
||||
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local sensitivityEvents = {
|
||||
UserGameSettings:GetPropertyChangedSignal("GamepadCameraSensitivity"),
|
||||
UserGameSettings:GetPropertyChangedSignal("MouseSensitivity"),
|
||||
UserGameSettings:GetPropertyChangedSignal("MouseSensitivityFirstPerson"),
|
||||
UserGameSettings:GetPropertyChangedSignal("MouseSensitivityThirdPerson"),
|
||||
}
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local SliderEntry = require(script.Parent.SliderEntry)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local FFlagCameraSensitivityAllowGranularKeyboardInput = game:DefineFastFlag("CameraSensitivityAllowGranularKeyboardInput", false)
|
||||
|
||||
-- remove with FFlagCameraSensitivityAllowGranularKeyboardInput
|
||||
local function guiToEngineSensitivity_DEPRECATED(guiSensitivity)
|
||||
local engineSensitivity
|
||||
if guiSensitivity <= 5 then
|
||||
engineSensitivity = 0.16 * guiSensitivity + 0.2
|
||||
else
|
||||
engineSensitivity = 0.6 * guiSensitivity - 2
|
||||
end
|
||||
|
||||
return math.clamp(
|
||||
engineSensitivity,
|
||||
MIN_ENGINE_MOUSE_SENSITIVITY,
|
||||
MAX_ENGINE_MOUSE_SENSITIVITY
|
||||
)
|
||||
end
|
||||
|
||||
local function guiToEngineSensitivity(guiSensitivity)
|
||||
local engineSensitivity
|
||||
if guiSensitivity <= 5 then
|
||||
engineSensitivity = 0.198 * guiSensitivity + 0.01
|
||||
else
|
||||
engineSensitivity = 0.6 * guiSensitivity - 2
|
||||
end
|
||||
|
||||
return math.clamp(
|
||||
engineSensitivity,
|
||||
MIN_ENGINE_MOUSE_SENSITIVITY,
|
||||
MAX_ENGINE_MOUSE_SENSITIVITY
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
-- remove with FFlagCameraSensitivityAllowGranularKeyboardInput
|
||||
local function engineToGuiSensitivity_DEPRECATED(engineSensitivity)
|
||||
local guiSensitivity
|
||||
if engineSensitivity <= 1 then
|
||||
guiSensitivity = math.floor((engineSensitivity - 0.2) / 0.16 + 0.5)
|
||||
else
|
||||
guiSensitivity = math.floor((engineSensitivity + 2) / 0.6 + 0.5)
|
||||
end
|
||||
|
||||
return math.clamp(
|
||||
guiSensitivity,
|
||||
MIN_GUI_MOUSE_SENSITIVITY,
|
||||
MAX_GUI_MOUSE_SENSITIVITY
|
||||
)
|
||||
end
|
||||
|
||||
local function engineToGuiSensitivity(engineSensitivity)
|
||||
local guiSensitivity
|
||||
if engineSensitivity <= 1 then
|
||||
guiSensitivity = (engineSensitivity - 0.01) / 0.198
|
||||
else
|
||||
guiSensitivity = (engineSensitivity + 2) / 0.6
|
||||
end
|
||||
|
||||
guiSensitivity = math.floor(guiSensitivity / GUI_MOUSE_SENSITIVITY_GRANULARITY + 0.5) * GUI_MOUSE_SENSITIVITY_GRANULARITY
|
||||
|
||||
return math.clamp(
|
||||
guiSensitivity,
|
||||
MIN_GUI_MOUSE_SENSITIVITY,
|
||||
MAX_GUI_MOUSE_SENSITIVITY
|
||||
)
|
||||
end
|
||||
|
||||
local CameraSensitivityEntry = Roact.PureComponent:extend("CameraSensitivityEntry")
|
||||
CameraSensitivityEntry.validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
function CameraSensitivityEntry:init()
|
||||
self.onSensitivityChanged = function()
|
||||
self:setState({
|
||||
-- TODO: Implement gamepad sensitivity.
|
||||
engineSensitivity = UserGameSettings.MouseSensitivityFirstPerson.X,
|
||||
})
|
||||
end
|
||||
|
||||
self.onSensitivityChanged()
|
||||
end
|
||||
|
||||
function CameraSensitivityEntry:render()
|
||||
local children = {
|
||||
-- TODO: Switch to gamepad sensitivity when appropriate
|
||||
CameraSensitivityMouseSlider = Roact.createElement(SliderEntry, {
|
||||
LayoutOrder = self.props.LayoutOrder,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.CameraSensitivity",
|
||||
value = FFlagCameraSensitivityAllowGranularKeyboardInput
|
||||
and engineToGuiSensitivity(self.state.engineSensitivity)
|
||||
or engineToGuiSensitivity_DEPRECATED(self.state.engineSensitivity),
|
||||
min = MIN_GUI_MOUSE_SENSITIVITY,
|
||||
max = MAX_GUI_MOUSE_SENSITIVITY,
|
||||
stepInterval = 1,
|
||||
keyboardInputStepInterval = FFlagCameraSensitivityAllowGranularKeyboardInput and GUI_MOUSE_SENSITIVITY_GRANULARITY or nil,
|
||||
valueChanged = function(value)
|
||||
local newEngineSensitivity = FFlagCameraSensitivityAllowGranularKeyboardInput
|
||||
and guiToEngineSensitivity(value)
|
||||
or guiToEngineSensitivity_DEPRECATED(value)
|
||||
local engineSensitivityVector = Vector2.new(newEngineSensitivity, newEngineSensitivity)
|
||||
UserGameSettings.MouseSensitivityFirstPerson = engineSensitivityVector
|
||||
UserGameSettings.MouseSensitivityThirdPerson = engineSensitivityVector
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
})
|
||||
}
|
||||
|
||||
for _, event in ipairs(sensitivityEvents) do
|
||||
children[tostring(event) .. "CameraSensitivityControlConnection"] = Roact.createElement(ExternalEventConnection, {
|
||||
event = event,
|
||||
callback = self.onSensitivityChanged,
|
||||
})
|
||||
end
|
||||
|
||||
return Roact.createFragment(children)
|
||||
end
|
||||
|
||||
return CameraSensitivityEntry
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local ThemedTextLabel = require(script.Parent.Parent.ThemedTextLabel)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
localizationKey = t.string,
|
||||
})
|
||||
|
||||
return function(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = props.LayoutOrder,
|
||||
Size = UDim2.new(1, 0, 0, 36),
|
||||
}, {
|
||||
Label = withLocalization({
|
||||
text = props.localizationKey
|
||||
})(function(localization)
|
||||
return Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Footer",
|
||||
themeKey = "TextEmphasis",
|
||||
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
Size = UDim2.new(1, -48, 0, 14),
|
||||
Text = localization.text,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
})
|
||||
end)
|
||||
})
|
||||
end
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
Position = t.UDim2,
|
||||
Size = t.UDim2,
|
||||
AnchorPoint = t.Vector2,
|
||||
})
|
||||
|
||||
return function(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withLocalization({
|
||||
lockedText = "CoreScripts.InGameMenu.GameSettings.LockedByDeveloper",
|
||||
})(function(localized)
|
||||
return Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "CaptionBody",
|
||||
TextTransparency = 0.5,
|
||||
Text = localized.lockedText,
|
||||
Size = UDim2.new(1, -72, 0.25, 0),
|
||||
Position = UDim2.new(0, 0, 1, 0),
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Bottom,
|
||||
})
|
||||
end)
|
||||
end
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
--[[
|
||||
Encompasses both the automatic graphics quality toggle _and_ the graphics
|
||||
quality slider.
|
||||
]]
|
||||
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local RenderSettings = settings().Rendering
|
||||
|
||||
local SavedQualityLevelChanged = UserGameSettings:GetPropertyChangedSignal("SavedQualityLevel") -- <- DEPRECATED: remove with FixGraphicsQuality
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local ToggleEntry = require(script.Parent.ToggleEntry)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local SliderEntry = require(script.Parent.SliderEntry)
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GetFixGraphicsQuality = require(RobloxGui.Modules.Flags.GetFixGraphicsQuality)
|
||||
|
||||
local SendNotification
|
||||
local RobloxTranslator
|
||||
local GraphicsQualityLevelChanged
|
||||
|
||||
if GetFixGraphicsQuality() then
|
||||
RobloxTranslator = require(RobloxGui:WaitForChild("Modules"):WaitForChild("RobloxTranslator"))
|
||||
GraphicsQualityLevelChanged = UserGameSettings:GetPropertyChangedSignal("GraphicsQualityLevel")
|
||||
end
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local SAVED_QUALITY_LEVELS = #Enum.SavedQualitySetting:GetEnumItems() - 1 -- <- DEPRECATED: remove with FixGraphicsQuality
|
||||
local GRAPHICS_QUALITY_LEVELS = 10
|
||||
|
||||
if GetFixGraphicsQuality() then
|
||||
GRAPHICS_QUALITY_LEVELS = RenderSettings:GetMaxQualityLevel() - 1
|
||||
-- Don't be fooled by the word "max". It's not the maximum level, it's a strict upper bound
|
||||
-- so if GetMaxQualityLevel() returns 22, that means the biggest the level can be is 21
|
||||
end
|
||||
|
||||
local function mapInteger(x, xMin, xMax, yMin, yMax) -- <- DEPRECATED: remove with FixGraphicsQuality
|
||||
return math.clamp(
|
||||
math.floor(yMin + (x - xMin)*(yMax - yMin)/(xMax - xMin)),
|
||||
yMin,
|
||||
yMax)
|
||||
end
|
||||
|
||||
--[[
|
||||
Generates the popup in the lower-right corner when the user changes the
|
||||
graphics quality level with F-keys.
|
||||
]]
|
||||
local function sendNotificationForGraphicsQualityLevelChange(message, level)
|
||||
if SendNotification == nil then
|
||||
SendNotification = RobloxGui:WaitForChild("SendNotificationInfo")
|
||||
end
|
||||
SendNotification:Fire({
|
||||
GroupName = "Graphics",
|
||||
Title = "Graphics Quality",
|
||||
Text = message,
|
||||
DetailText = message,
|
||||
Image = "",
|
||||
Duration = 2
|
||||
})
|
||||
end
|
||||
|
||||
--[[
|
||||
Takes two arguments: newValue, delta. If delta is non-zero, constructs a message for the pop-up
|
||||
notification (based on whether delta is positive or negative) to inform the user that the level
|
||||
incrememnted / decremented to the new value.
|
||||
]]
|
||||
local function notifyForLevelChange(newValue, delta)
|
||||
if delta > 0 then
|
||||
sendNotificationForGraphicsQualityLevelChange(
|
||||
RobloxTranslator:FormatByKey("NotificationScrip2.onCurrentGraphicsQualityLevelChanged.Increased", {RBX_NUMBER = tostring(newValue)}),
|
||||
newValue)
|
||||
elseif delta < 0 then
|
||||
sendNotificationForGraphicsQualityLevelChange(
|
||||
RobloxTranslator:FormatByKey("NotificationScrip2.onCurrentGraphicsQualityLevelChanged.Decreased", {RBX_NUMBER = tostring(newValue)}),
|
||||
newValue)
|
||||
end
|
||||
end
|
||||
|
||||
local GraphicsQualityEntry = Roact.PureComponent:extend("GraphicsQualityEntry")
|
||||
GraphicsQualityEntry.validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
function GraphicsQualityEntry:init()
|
||||
local isAutomatic
|
||||
if GetFixGraphicsQuality() then
|
||||
local quality = UserGameSettings.GraphicsQualityLevel
|
||||
isAutomatic = quality == 0
|
||||
self:setState({
|
||||
quality = isAutomatic and 5 or UserGameSettings.GraphicsQualityLevel,
|
||||
automatic = isAutomatic,
|
||||
})
|
||||
self.onQualityChanged = function()
|
||||
local quality = UserGameSettings.GraphicsQualityLevel
|
||||
self:setState({
|
||||
quality = quality ~= 0 and quality or nil,
|
||||
automatic = quality == 0,
|
||||
})
|
||||
end
|
||||
else
|
||||
isAutomatic = UserGameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic
|
||||
self:setState({
|
||||
quality = isAutomatic and 5 or UserGameSettings.SavedQualityLevel.Value,
|
||||
automatic = isAutomatic,
|
||||
})
|
||||
|
||||
self.onQualityChanged = function()
|
||||
local newSavedQuality = UserGameSettings.SavedQualityLevel
|
||||
local changedToAutomatic = newSavedQuality == Enum.SavedQualitySetting.Automatic
|
||||
|
||||
self:setState({
|
||||
quality = not changedToAutomatic and newSavedQuality.Value or nil,
|
||||
automatic = newSavedQuality == Enum.SavedQualitySetting.Automatic,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if isAutomatic then
|
||||
self:setAutomaticQualityLevel()
|
||||
else
|
||||
self:setManualQualityLevel(self.state.quality)
|
||||
end
|
||||
|
||||
if GetFixGraphicsQuality() then
|
||||
--[[
|
||||
Gets called when the user hits F10 / Shift-F10 to adjust the graphcs quality level.
|
||||
|
||||
Calls setManualQualityLevel to set the new level. Note: setManualQualityLevel checks whether the new value
|
||||
it's given is in bounds. The returned newValue and delta indicate the change that actually takes place
|
||||
We use newValue and delta to determine what notification to show.
|
||||
]]
|
||||
game.GraphicsQualityChangeRequest:connect(
|
||||
function(isIncrease)
|
||||
local current = UserGameSettings.GraphicsQualityLevel
|
||||
if current ~= 0 then
|
||||
local newValue, delta = self:setManualQualityLevel(current + (isIncrease and 1 or -1))
|
||||
notifyForLevelChange(newValue, delta)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function GraphicsQualityEntry:setAutomaticQualityLevel()
|
||||
if GetFixGraphicsQuality() then
|
||||
UserGameSettings.GraphicsQualityLevel = 0
|
||||
RenderSettings.QualityLevel = 0
|
||||
else
|
||||
UserGameSettings.SavedQualityLevel = Enum.SavedQualitySetting.Automatic
|
||||
RenderSettings.QualityLevel = 0
|
||||
end
|
||||
end
|
||||
|
||||
function GraphicsQualityEntry:setManualQualityLevel(manualQualityLevel)
|
||||
if GetFixGraphicsQuality() then
|
||||
local newValue = math.clamp(manualQualityLevel, 1, GRAPHICS_QUALITY_LEVELS)
|
||||
local oldValue = UserGameSettings.GraphicsQualityLevel
|
||||
|
||||
UserGameSettings.GraphicsQualityLevel = newValue
|
||||
RenderSettings.QualityLevel = newValue
|
||||
|
||||
return newValue, newValue - oldValue
|
||||
else
|
||||
local renderQualityLevel = mapInteger(
|
||||
manualQualityLevel,
|
||||
1, SAVED_QUALITY_LEVELS,
|
||||
-- Quality levels are zero-based; GetMaxQualityLevel reports 22, even
|
||||
-- though 21 is the maximum.
|
||||
1, RenderSettings:GetMaxQualityLevel() - 1)
|
||||
UserGameSettings.SavedQualityLevel = manualQualityLevel
|
||||
RenderSettings.QualityLevel = renderQualityLevel
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function GraphicsQualityEntry:render()
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, 56 + 100),
|
||||
LayoutOrder = self.props.LayoutOrder,
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
AutoToggle = Roact.createElement(ToggleEntry, {
|
||||
LayoutOrder = 1,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.AutoGraphicsQuality",
|
||||
checked = self.state.automatic,
|
||||
onToggled = function()
|
||||
if self.state.automatic then
|
||||
self:setManualQualityLevel(self.state.quality)
|
||||
else
|
||||
self:setAutomaticQualityLevel()
|
||||
end
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
}),
|
||||
Slider = Roact.createElement(SliderEntry, {
|
||||
LayoutOrder = 2,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.ManualGraphicsQuality",
|
||||
min = 1,
|
||||
max = GRAPHICS_QUALITY_LEVELS,
|
||||
stepInterval = 1,
|
||||
value = self.state.quality,
|
||||
disabled = self.state.automatic,
|
||||
valueChanged = function(newValue)
|
||||
self:setManualQualityLevel(newValue)
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
}),
|
||||
QualityListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = GetFixGraphicsQuality() and GraphicsQualityLevelChanged or SavedQualityLevelChanged,
|
||||
callback = self.onQualityChanged,
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return GraphicsQualityEntry
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
return function()
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local GetFixGraphicsQuality = require(RobloxGui.Modules.Flags.GetFixGraphicsQuality)
|
||||
local SendNotificationInfo = Instance.new("BindableEvent")
|
||||
SendNotificationInfo.Name = "SendNotificationInfo"
|
||||
SendNotificationInfo.Parent = RobloxGui
|
||||
|
||||
local GraphicsQualityEntry = require(script.Parent.GraphicsQualityEntry)
|
||||
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local RenderSettings = settings().Rendering
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
GraphicsQualityEntry = Roact.createElement(GraphicsQualityEntry, {
|
||||
LayoutOrder = 2,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
|
||||
UserGameSettings.SavedQualityLevel = 10
|
||||
RenderSettings.QualityLevel = 5
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should set the rendering setting to the saved quality level when initalized", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
GraphicsQualityEntry = Roact.createElement(GraphicsQualityEntry, {
|
||||
LayoutOrder = 2,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
if GetFixGraphicsQuality() then
|
||||
UserGameSettings.GraphicsQualityLevel = 11
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
expect(RenderSettings.QualityLevel).to.equal(Enum.QualityLevel.Level11)
|
||||
Roact.unmount(instance)
|
||||
|
||||
UserGameSettings.GraphicsQualityLevel = 21
|
||||
instance = Roact.mount(element)
|
||||
expect(RenderSettings.QualityLevel).to.equal(Enum.QualityLevel.Level21)
|
||||
Roact.unmount(instance)
|
||||
else
|
||||
UserGameSettings.SavedQualityLevel = Enum.SavedQualitySetting.QualityLevel2
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
expect(RenderSettings.QualityLevel).to.equal(Enum.QualityLevel.Level03)
|
||||
Roact.unmount(instance)
|
||||
|
||||
UserGameSettings.SavedQualityLevel = Enum.SavedQualitySetting.QualityLevel10
|
||||
instance = Roact.mount(element)
|
||||
expect(RenderSettings.QualityLevel).to.equal(Enum.QualityLevel.Level21)
|
||||
Roact.unmount(instance)
|
||||
end
|
||||
end)
|
||||
end
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
AnchorPoint = t.optional(t.Vector2),
|
||||
Position = t.optional(t.UDim2),
|
||||
Size = t.optional(t.UDim2),
|
||||
localizationKey = t.string,
|
||||
})
|
||||
|
||||
return function(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withLocalization({
|
||||
text = props.localizationKey,
|
||||
})(function(localized)
|
||||
return Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Body",
|
||||
themeKey = "TextDefault",
|
||||
|
||||
AnchorPoint = props.AnchorPoint or Vector2.new(0, 0.5),
|
||||
Position = props.Position or UDim2.new(0, 0, 0.5, 0),
|
||||
Size = props.Size or UDim2.new(1, -72, 0.5, 0),
|
||||
Text = localized.text,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
})
|
||||
end)
|
||||
end
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Players = game:GetService("Players")
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
|
||||
local ComputerMovementModeChanged = UserGameSettings:GetPropertyChangedSignal("ComputerMovementMode")
|
||||
|
||||
local localPlayer = Players.LocalPlayer
|
||||
local playerScripts = localPlayer:WaitForChild("PlayerScripts")
|
||||
local DevComputerMovementModeChanged = localPlayer:GetPropertyChangedSignal("DevComputerMovementMode")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local DropDownSelection = require(InGameMenu.Components.DropDownSelection)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
|
||||
local InputLabel = require(script.Parent.InputLabel)
|
||||
local DeveloperLockLabel = require(script.Parent.DeveloperLockLabel)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
local MOVEMENT_MODE_LOCALIZATION_KEYS = {
|
||||
[Enum.ComputerMovementMode.KeyboardMouse] = "CoreScripts.InGameMenu.GameSettings.ComputerMoveModeKeyboardMouse",
|
||||
[Enum.ComputerMovementMode.ClickToMove] = "CoreScripts.InGameMenu.GameSettings.ComputerMoveModeClickToMove",
|
||||
[Enum.ComputerMovementMode.Default] = "CoreScripts.InGameMenu.GameSettings.Default",
|
||||
}
|
||||
|
||||
local DEV_MOVEMENT_MODE_LOCALIZATION_KEYS = {
|
||||
[Enum.DevComputerMovementMode.KeyboardMouse] = "CoreScripts.InGameMenu.GameSettings.ComputerMoveModeKeyboardMouse",
|
||||
[Enum.DevComputerMovementMode.ClickToMove] = "CoreScripts.InGameMenu.GameSettings.ComputerMoveModeClickToMove",
|
||||
}
|
||||
|
||||
local MovementModeEntry = Roact.PureComponent:extend("MovementModeEntry")
|
||||
MovementModeEntry.validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
function MovementModeEntry:init()
|
||||
self:setState({
|
||||
selectedComputerMode = UserGameSettings.ComputerMovementMode,
|
||||
computerOptions = playerScripts:GetRegisteredComputerMovementModes(),
|
||||
developerComputerMode = localPlayer.DevComputerMovementMode,
|
||||
})
|
||||
end
|
||||
|
||||
function MovementModeEntry:render()
|
||||
-- TODO: Support gamepad and touch controls.
|
||||
|
||||
local result = {
|
||||
ComputerMovementModeListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = ComputerMovementModeChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
selectedComputerMode = UserGameSettings.ComputerMovementMode,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
ComputerMovementModeRegisteredListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = playerScripts.ComputerMovementModeRegistered,
|
||||
callback = function()
|
||||
self:setState({
|
||||
computerOptions = playerScripts:GetRegisteredComputerMovementModes(),
|
||||
})
|
||||
end,
|
||||
}),
|
||||
DevComputerMovementMode = Roact.createElement(ExternalEventConnection, {
|
||||
event = DevComputerMovementModeChanged,
|
||||
callback = function()
|
||||
self:setState({
|
||||
developerComputerMode = localPlayer.DevComputerMovementMode,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
}
|
||||
|
||||
if #self.state.computerOptions == 0 then
|
||||
return Roact.createFragment(result)
|
||||
end
|
||||
|
||||
local disabled = self.state.developerComputerMode ~= Enum.DevComputerMovementMode.UserChoice
|
||||
local optionTexts = {}
|
||||
local selectedIndex = 0
|
||||
|
||||
if not disabled then
|
||||
for index, enum in ipairs(self.state.computerOptions) do
|
||||
optionTexts[index] = MOVEMENT_MODE_LOCALIZATION_KEYS[enum]
|
||||
assert(optionTexts[index] ~= nil, "Movement mode " .. enum.Name .. " has no localization key")
|
||||
|
||||
if self.state.selectedComputerMode.Value == enum.Value then
|
||||
selectedIndex = index
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
result.MovementModeEntrySelector = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, 44 + 56 + 20),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = self.props.LayoutOrder,
|
||||
ZIndex = 2,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, 24),
|
||||
PaddingRight = UDim.new(0, 24),
|
||||
}),
|
||||
InputLabel = Roact.createElement(InputLabel, {
|
||||
localizationKey = "CoreScripts.InGameMenu.GameSettings.MovementMode",
|
||||
Size = UDim2.new(1, 0, 0, 56),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
AnchorPoint = Vector2.new(0, 0),
|
||||
}),
|
||||
Dropdown = withLocalization({
|
||||
placeholder = disabled and DEV_MOVEMENT_MODE_LOCALIZATION_KEYS[self.state.developerComputerMode] or nil
|
||||
})(function(localized)
|
||||
return Roact.createElement(DropDownSelection, {
|
||||
Size = UDim2.new(1, 0, 0, 44),
|
||||
Position = UDim2.new(0, 0, 0, 56),
|
||||
selections = optionTexts,
|
||||
selectedIndex = selectedIndex,
|
||||
placeHolderText = localized.placeholder,
|
||||
enabled = not disabled,
|
||||
localize = true,
|
||||
selectionChanged = function(newSelection)
|
||||
UserGameSettings.ComputerMovementMode = self.state.computerOptions[newSelection]
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
})
|
||||
end),
|
||||
LockedLabel = disabled and Roact.createElement(DeveloperLockLabel, {
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
Position = UDim2.new(0, 0, 1, 0),
|
||||
Size = UDim2.new(1, -72, 0.25, 0),
|
||||
}),
|
||||
})
|
||||
|
||||
return Roact.createFragment(result)
|
||||
end
|
||||
|
||||
return MovementModeEntry
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Players = game:GetService("Players")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local MovementModeEntry = require(script.Parent.MovementModeEntry)
|
||||
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local localPlayer = Players.LocalPlayer
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
MovementModeEntry = Roact.createElement(MovementModeEntry, {
|
||||
LayoutOrder = 2,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
|
||||
UserGameSettings.ComputerMovementMode = Enum.ComputerMovementMode.ClickToMove
|
||||
localPlayer.DevComputerMovementMode = Enum.DevComputerMovementMode.ClickToMove
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local InputLabel = require(script.Parent.InputLabel)
|
||||
local SliderWithInput = require(InGameMenu.Components.SliderWithInput)
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
labelKey = t.string,
|
||||
min = t.number,
|
||||
max = t.number,
|
||||
stepInterval = t.number,
|
||||
keyboardInputStepInterval = t.optional(t.numberPositive),
|
||||
value = t.number,
|
||||
disabled = t.optional(t.boolean),
|
||||
valueChanged = t.callback,
|
||||
})
|
||||
|
||||
local function SliderEntry(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, 92),
|
||||
LayoutOrder = props.LayoutOrder,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, 24),
|
||||
PaddingTop = UDim.new(0, 12),
|
||||
PaddingRight = UDim.new(0, 30),
|
||||
PaddingBottom = UDim.new(0, 24),
|
||||
}),
|
||||
Label = Roact.createElement(InputLabel, {
|
||||
Size = UDim2.new(1, 0, 0, 24),
|
||||
localizationKey = props.labelKey,
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
AnchorPoint = Vector2.new(0, 0),
|
||||
}),
|
||||
Slider = Roact.createElement(SliderWithInput, {
|
||||
min = props.min,
|
||||
max = props.max,
|
||||
stepInterval = props.stepInterval,
|
||||
keyboardInputStepInterval = props.keyboardInputStepInterval,
|
||||
value = props.value,
|
||||
Position = UDim2.new(0, 0, 0, 32),
|
||||
disabled = props.disabled,
|
||||
valueChanged = props.valueChanged,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return SliderEntry
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local SliderEntry = require(script.Parent.SliderEntry)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
SliderEntry = Roact.createElement(SliderEntry, {
|
||||
LayoutOrder = 2,
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.CameraSensitivity",
|
||||
min = 1,
|
||||
max = 10,
|
||||
stepInterval = 1,
|
||||
value = 5,
|
||||
disabled = false,
|
||||
valueChanged = function()
|
||||
print("Value changed")
|
||||
end,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local ToggleSwitch = require(InGameMenu.Components.ToggleSwitch)
|
||||
|
||||
local InputLabel = require(script.Parent.InputLabel)
|
||||
local DeveloperLockLabel = require(script.Parent.DeveloperLockLabel)
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
labelKey = t.string,
|
||||
lockedToOff = t.optional(t.boolean),
|
||||
subtextEnabled = t.optional(t.boolean),
|
||||
subtextKey = t.optional(t.string),
|
||||
checked = t.boolean,
|
||||
onToggled = t.callback,
|
||||
})
|
||||
|
||||
local function ToggleEntry(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = props.LayoutOrder,
|
||||
Size = UDim2.new(1, 0, 0, 56),
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingRight = UDim.new(0, 30),
|
||||
PaddingLeft = UDim.new(0, 24),
|
||||
}),
|
||||
ControlLabel = Roact.createElement(InputLabel, {
|
||||
localizationKey = props.labelKey,
|
||||
}),
|
||||
LockedLabel = props.lockedToOff and Roact.createElement(DeveloperLockLabel, {
|
||||
Size = UDim2.new(1, -72, 0.25, 0),
|
||||
Position = UDim2.new(0, 0, 1, 0),
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
}),
|
||||
SubtextLabel = props.subtextEnabled and withLocalization({
|
||||
text = props.subtextKey,
|
||||
})(function(localization)
|
||||
return Roact.createElement(ThemedTextLabel, {
|
||||
Text = localization.text,
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "CaptionBody",
|
||||
TextTransparency = 0.5,
|
||||
Size = UDim2.new(1, -72, 0.25, 0),
|
||||
Position = UDim2.new(0, 0, 1, 0),
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Bottom,
|
||||
})
|
||||
end),
|
||||
Toggle = Roact.createElement(ToggleSwitch, {
|
||||
Position = UDim2.new(1, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(1, 0.5),
|
||||
checked = props.checked and not props.lockedToOff,
|
||||
onToggled = props.onToggled,
|
||||
disabled = props.lockedToOff,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return ToggleEntry
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
local LATEST_PLACE_VERSION = "CoreScripts.InGameMenu.GameSettings.PlaceVersionWithLatest"
|
||||
local PLACE_VERSION = "CoreScripts.InGameMenu.GameSettings.PlaceVersion"
|
||||
|
||||
local ContentProvider = game:GetService("ContentProvider")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local HttpRbxApiService = game:GetService("HttpRbxApiService")
|
||||
local HttpService = game:GetService("HttpService")
|
||||
local Players = game:GetService("Players")
|
||||
local RobloxReplicatedStorage = game:GetService("RobloxReplicatedStorage")
|
||||
local RunService = game:GetService("RunService")
|
||||
local StarterPlayer = game:GetService("StarterPlayer")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local PlayerPermissions = require(CoreGui.RobloxGui.Modules.PlayerPermissionsModule)
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local Divider = require(InGameMenu.Components.Divider)
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local canGetCoreScriptVersion = game:getEngineFeature("CoreScriptVersionEnabled")
|
||||
|
||||
local VersionReporter = Roact.PureComponent:extend("VersionReporter")
|
||||
VersionReporter.validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
local function inferPlayerScriptStatus(starterPlayerScripts)
|
||||
local loader = starterPlayerScripts:FindFirstChild("PlayerScriptsLoader")
|
||||
local playerModule = starterPlayerScripts:FindFirstChild("PlayerModule")
|
||||
|
||||
if loader ~= nil and playerModule ~= nil then
|
||||
if not playerModule.Archivable and not loader.Archivable then
|
||||
return "PlayerScript Status: Default"
|
||||
else
|
||||
return "PlayerScript Status: Possibly Custom"
|
||||
end
|
||||
end
|
||||
|
||||
local cameraScript = starterPlayerScripts:FindFirstChild("CameraScript")
|
||||
local controlScript = starterPlayerScripts:FindFirstChild("ControlScript")
|
||||
if cameraScript ~= nil or controlScript ~= nil then
|
||||
return "PlayerScript Status: Custom Old"
|
||||
else
|
||||
return "PlayerScript Status: Custom"
|
||||
end
|
||||
end
|
||||
|
||||
function VersionReporter:init()
|
||||
local initalPlayerscriptStatus = "PlayerScript Status: Unknown"
|
||||
local starterPlayerScripts = StarterPlayer:FindFirstChild("StarterPlayerScripts")
|
||||
if starterPlayerScripts then
|
||||
initalPlayerscriptStatus = inferPlayerScriptStatus(starterPlayerScripts)
|
||||
else
|
||||
self.playerScriptsAddedConnection = StarterPlayer.ChildAdded:Connect(function(child)
|
||||
if child.Name == "StarterPlayerScripts" then
|
||||
self.playerScriptsAddedConnection:Disconnect()
|
||||
self.playerScriptsAddedConnection = nil
|
||||
self:setState({
|
||||
playerScriptStatus = inferPlayerScriptStatus(child),
|
||||
})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
self:setState({
|
||||
baseUrl = ContentProvider.BaseUrl,
|
||||
clientVersion = RunService:GetRobloxVersion(),
|
||||
isAdmin = false,
|
||||
latestPlaceVersion = nil,
|
||||
placeVersion = game.PlaceVersion,
|
||||
serverVersion = nil,
|
||||
playerScriptStatus = initalPlayerscriptStatus,
|
||||
coreScriptVersion = canGetCoreScriptVersion and RunService:GetCoreScriptVersion() or nil,
|
||||
})
|
||||
end
|
||||
|
||||
function VersionReporter:render()
|
||||
local haveServerVersion = self.state.serverVersion ~= nil
|
||||
local haveLatestPlaceVersion = self.state.latestPlaceVersion ~= nil
|
||||
local isTestEnvironment = not self.state.baseUrl:find("www.roblox.com")
|
||||
local showEnvironment = isTestEnvironment
|
||||
local showPlayerScriptStatus = (isTestEnvironment or self.state.isAdmin)
|
||||
|
||||
return withLocalization({
|
||||
clientVersion = {
|
||||
"CoreScripts.InGameMenu.GameSettings.ClientVersion",
|
||||
CLIENT_VERSION = self.state.clientVersion,
|
||||
},
|
||||
serverVersion = {
|
||||
"CoreScripts.InGameMenu.GameSettings.ServerVersion",
|
||||
SERVER_VERSION = self.state.serverVersion,
|
||||
},
|
||||
placeVersion = {
|
||||
haveLatestPlaceVersion and LATEST_PLACE_VERSION or PLACE_VERSION,
|
||||
PLACE_VERSION = self.state.placeVersion,
|
||||
LATEST_PLACE_VERSION = self.state.latestPlaceVersion,
|
||||
},
|
||||
coreScriptVersion = canGetCoreScriptVersion and {
|
||||
"CoreScripts.InGameMenu.GameSettings.ClientCoreScriptVersion",
|
||||
CLIENT_CORESCRIPT_VERSION = self.state.coreScriptVersion,
|
||||
} or nil
|
||||
})(function(localized)
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, 80),
|
||||
LayoutOrder = self.props.LayoutOrder,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingTop = UDim.new(0, 14),
|
||||
PaddingLeft = UDim.new(0, 24),
|
||||
PaddingBottom = UDim.new(0, 14),
|
||||
}),
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, 10),
|
||||
}),
|
||||
ClientVersion = Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "Footer",
|
||||
Text = localized.clientVersion,
|
||||
LayoutOrder = 1,
|
||||
Size = UDim2.new(1, -24, 0, 14),
|
||||
}),
|
||||
ServerVersion = Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "Footer",
|
||||
Text = haveServerVersion and localized.serverVersion or "",
|
||||
LayoutOrder = 2,
|
||||
Size = UDim2.new(1, -24, 0, 14),
|
||||
}, {
|
||||
not haveServerVersion and Roact.createElement(UIBlox.Loading.ShimmerPanel, {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
shimmerSpeed = 2,
|
||||
}),
|
||||
}),
|
||||
PlaceVersion = Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "Footer",
|
||||
Text = localized.placeVersion,
|
||||
LayoutOrder = 3,
|
||||
Size = UDim2.new(1, -24, 0, 14),
|
||||
}),
|
||||
Divider = (showEnvironment or showPlayerScriptStatus) and Roact.createElement(Divider, {
|
||||
Size = UDim2.new(1, 0, 0, 1),
|
||||
LayoutOrder = 4,
|
||||
}),
|
||||
PlayerScriptStatus = showPlayerScriptStatus and Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "Footer",
|
||||
Text = self.state.playerScriptStatus,
|
||||
LayoutOrder = 5,
|
||||
Size = UDim2.new(1, -24, 0, 14),
|
||||
}),
|
||||
Environment = showEnvironment and Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "Footer",
|
||||
Text = ("Base URL: %s"):format(self.state.baseUrl),
|
||||
LayoutOrder = 6,
|
||||
Size = UDim2.new(1, -24, 0, 14),
|
||||
}),
|
||||
Divider2 = canGetCoreScriptVersion and Roact.createElement(Divider, {
|
||||
Size = UDim2.new(1, 0, 0, 1),
|
||||
LayoutOrder = 7,
|
||||
}),
|
||||
CoreScriptVersion = canGetCoreScriptVersion and Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextDefault",
|
||||
fontKey = "Footer",
|
||||
Text = localized.coreScriptVersion,
|
||||
LayoutOrder = 8,
|
||||
Size = UDim2.new(1, -24, 0, 14),
|
||||
}),
|
||||
VersionWatcher = Roact.createElement(ExternalEventConnection, {
|
||||
event = game:GetPropertyChangedSignal("PlaceVersion"),
|
||||
callback = function()
|
||||
self:setState({
|
||||
placeVersion = game.PlaceVersion,
|
||||
})
|
||||
end,
|
||||
})
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
function VersionReporter:didMount()
|
||||
self.mounted = true
|
||||
|
||||
coroutine.wrap(function()
|
||||
local serverVersionRemote = RobloxReplicatedStorage:WaitForChild("GetServerVersion", math.huge)
|
||||
local version = serverVersionRemote:InvokeServer()
|
||||
|
||||
if self.mounted then
|
||||
self:setState({
|
||||
serverVersion = version,
|
||||
})
|
||||
end
|
||||
end)()
|
||||
|
||||
coroutine.wrap(function()
|
||||
pcall(function()
|
||||
local json = HttpRbxApiService:GetAsync(string.format("assets/%d/versions", game.PlaceId))
|
||||
local versionData = HttpService:JSONDecode(json)
|
||||
local latestVersion = versionData[1].VersionNumber
|
||||
|
||||
if self.mounted then
|
||||
self:setState({
|
||||
latestPlaceVersion = latestVersion,
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)()
|
||||
|
||||
coroutine.wrap(function()
|
||||
local isAdmin = PlayerPermissions.IsPlayerAdminAsync(Players.LocalPlayer)
|
||||
|
||||
if self.mounted then
|
||||
self:setState({
|
||||
isAdmin = isAdmin,
|
||||
})
|
||||
end
|
||||
end)()
|
||||
end
|
||||
|
||||
function VersionReporter:willUnmount()
|
||||
self.mounted = false
|
||||
if self.playerScriptsAddedConnection then
|
||||
self.playerScriptsAddedConnection:Disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
return VersionReporter
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local UserGameSettings = UserSettings():GetService("UserGameSettings")
|
||||
local MasterVolumeChanged = UserGameSettings:GetPropertyChangedSignal("MasterVolume")
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local ExternalEventConnection = require(InGameMenu.Utility.ExternalEventConnection)
|
||||
local SliderEntry = require(script.Parent.SliderEntry)
|
||||
|
||||
local SendAnalytics = require(InGameMenu.Utility.SendAnalytics)
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
|
||||
if CoreGui.RobloxGui:FindFirstChild("Sounds") == nil then
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Sounds"
|
||||
folder.Parent = CoreGui.RobloxGui
|
||||
end
|
||||
|
||||
local volumeTest = Instance.new("Sound", CoreGui.RobloxGui.Sounds)
|
||||
volumeTest.Name = "VolumeChangeSound"
|
||||
volumeTest.Volume = 1
|
||||
volumeTest.SoundId = "rbxasset://sounds/uuhhh.mp3"
|
||||
|
||||
local VolumeEntry = Roact.PureComponent:extend("VolumeEntry")
|
||||
VolumeEntry.validateProps = t.strictInterface({
|
||||
LayoutOrder = t.integer,
|
||||
})
|
||||
|
||||
function VolumeEntry:init()
|
||||
self.onVolumeChanged = function()
|
||||
self:setState({
|
||||
volume = math.floor(UserGameSettings.MasterVolume * 10 + 0.5)
|
||||
})
|
||||
end
|
||||
|
||||
self.onVolumeChanged()
|
||||
end
|
||||
|
||||
function VolumeEntry:render()
|
||||
return Roact.createFragment({
|
||||
VolumeSlider = Roact.createElement(SliderEntry, {
|
||||
labelKey = "CoreScripts.InGameMenu.GameSettings.Volume",
|
||||
LayoutOrder = self.props.LayoutOrder,
|
||||
min = 0,
|
||||
max = 10,
|
||||
stepInterval = 1,
|
||||
value = self.state.volume,
|
||||
valueChanged = function(newValue)
|
||||
UserGameSettings.MasterVolume = newValue / 10
|
||||
volumeTest:Play()
|
||||
SendAnalytics(Constants.AnalyticsSettingsChangeName, nil, {}, true)
|
||||
end,
|
||||
}),
|
||||
VolumeListener = Roact.createElement(ExternalEventConnection, {
|
||||
event = MasterVolumeChanged,
|
||||
callback = self.onVolumeChanged,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return VolumeEntry
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
|
||||
local ThemedTextLabel = require(script.Parent.ThemedTextLabel)
|
||||
local Divider = require(script.Parent.Divider)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local DIALOG_WIDTH = 312
|
||||
local DIALOG_PADDING = 24
|
||||
|
||||
local MODAL_DISMISS_ACTION = "InGameMenuModalDismiss"
|
||||
|
||||
local InfoDialog = Roact.PureComponent:extend("InfoDialog")
|
||||
|
||||
InfoDialog.validateProps = t.strictInterface({
|
||||
bodyText = t.string,
|
||||
dismissText = t.string,
|
||||
titleText = t.string,
|
||||
iconImage = t.union(t.string, t.table),
|
||||
|
||||
onDismiss = t.callback,
|
||||
visible = t.boolean,
|
||||
})
|
||||
|
||||
function InfoDialog:render()
|
||||
local props = self.props
|
||||
|
||||
return withStyle(function(style)
|
||||
local bodyFont = style.Font.Body.Font
|
||||
local bodyFontSize = style.Font.Body.RelativeSize * style.Font.BaseSize
|
||||
local bodyText = props.bodyText
|
||||
local textHeight = TextService:GetTextSize(
|
||||
bodyText,
|
||||
bodyFontSize,
|
||||
bodyFont,
|
||||
Vector2.new(DIALOG_WIDTH - DIALOG_PADDING * 2, math.huge)
|
||||
).Y
|
||||
|
||||
-- 24px padding top, 24px padding bottom
|
||||
-- Minimum height of two lines of text.
|
||||
-- TODO: Line height!
|
||||
local bodyTextContainerHeight = DIALOG_PADDING * 2 + math.max(textHeight, bodyFontSize * 2)
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = 8,
|
||||
Visible = props.visible,
|
||||
}, {
|
||||
Overlay = Roact.createElement("TextButton", {
|
||||
AutoButtonColor = false,
|
||||
BackgroundColor3 = style.Theme.Overlay.Color,
|
||||
BackgroundTransparency = style.Theme.Overlay.Transparency,
|
||||
BorderSizePixel = 0,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Text = "",
|
||||
}),
|
||||
DialogMainFrame = Roact.createElement(ImageSetLabel, {
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.RoundedRect.Image,
|
||||
ImageColor3 = style.Theme.BackgroundUIDefault.Color,
|
||||
ImageTransparency = style.Theme.BackgroundUIDefault.Transparency,
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
ScaleType = Assets.Images.RoundedRect.ScaleType,
|
||||
-- button (36px), bottom padding (24px), top padding (10px)
|
||||
-- height: title bar (48px), divider (1px), body text height, icon(36px)
|
||||
Size = UDim2.new(0, DIALOG_WIDTH, 0, 48 + 1 + bodyTextContainerHeight + 36 + 36 + DIALOG_PADDING + 10),
|
||||
SliceCenter = Assets.Images.RoundedRect.SliceCenter,
|
||||
}, {
|
||||
Padding = Roact.createElement("UIPadding", {
|
||||
PaddingTop = UDim.new(0, 10),
|
||||
PaddingBottom = UDim.new(0, DIALOG_PADDING),
|
||||
PaddingLeft = UDim.new(0, DIALOG_PADDING),
|
||||
PaddingRight = UDim.new(0, DIALOG_PADDING),
|
||||
}),
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
Icon = Roact.createElement(ImageSetLabel, {
|
||||
Size = UDim2.new(0, 36, 0, 36),
|
||||
BackgroundTransparency = 1,
|
||||
Image = self.props.iconImage,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
Title = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Header1",
|
||||
themeKey = "TextEmphasis",
|
||||
|
||||
LayoutOrder = 2,
|
||||
Size = UDim2.new(1, 0, 0, 48),
|
||||
Text = props.titleText,
|
||||
}),
|
||||
Divider = Roact.createElement(Divider, {
|
||||
LayoutOrder = 3,
|
||||
}),
|
||||
BodyTextContainer = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 4,
|
||||
Size = UDim2.new(1, 0, 0, bodyTextContainerHeight),
|
||||
}, {
|
||||
BodyText = Roact.createElement(ThemedTextLabel, {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Text = props.bodyText,
|
||||
TextWrapped = true,
|
||||
})
|
||||
}),
|
||||
ConfirmButton = Roact.createElement(UIBlox.App.Button.PrimarySystemButton, {
|
||||
layoutOrder = 5,
|
||||
size = UDim2.new(1, 0, 0, 36),
|
||||
onActivated = props.onDismiss,
|
||||
text = props.dismissText,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
function InfoDialog:bindActions()
|
||||
local function dismissFunc(actionName, inputState, input)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
self.props.onDismiss()
|
||||
end
|
||||
end
|
||||
|
||||
ContextActionService:BindCoreAction(
|
||||
MODAL_DISMISS_ACTION, dismissFunc, false, Enum.KeyCode.Escape)
|
||||
end
|
||||
|
||||
function InfoDialog:unbindActions()
|
||||
ContextActionService:UnbindCoreAction(MODAL_DISMISS_ACTION)
|
||||
end
|
||||
|
||||
function InfoDialog:didMount()
|
||||
if self.props.visible then
|
||||
self:bindActions()
|
||||
end
|
||||
end
|
||||
|
||||
function InfoDialog:didUpdate()
|
||||
if self.props.visible then
|
||||
self:bindActions()
|
||||
else
|
||||
self:unbindActions()
|
||||
end
|
||||
end
|
||||
|
||||
function InfoDialog:willUnmount()
|
||||
self:unbindActions()
|
||||
end
|
||||
|
||||
return InfoDialog
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local InfoDialog = require(script.Parent.InfoDialog)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
GameIconHeader = Roact.createElement(InfoDialog, {
|
||||
bodyText = "Hello world!",
|
||||
dismissText = "Okay",
|
||||
titleText = "Title",
|
||||
iconImage = "",
|
||||
|
||||
onDismiss = function()
|
||||
print("on dismiss")
|
||||
end,
|
||||
visible = true,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local SetCurrentPage = require(InGameMenu.Actions.SetCurrentPage)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local CONTAINER_WIDTH = 304
|
||||
local TEXT_PADDING_TOP = 4
|
||||
local TEXT_PADDING_BOTTOM = 16
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
switchToPlayers = t.callback,
|
||||
})
|
||||
|
||||
local function AddFriendsNow(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withStyle(function(style)
|
||||
return withLocalization({
|
||||
noFriendsText = "CoreScripts.InGameMenu.InviteFriends.NoFriends",
|
||||
makeFriendsNow = "CoreScripts.InGameMenu.InviteFriends.MakeFriendsNow",
|
||||
})(function(localized)
|
||||
local bodyFont = style.Font.Body.Font
|
||||
local bodyFontSize = style.Font.Body.RelativeSize * style.Font.BaseSize
|
||||
local textHeight = TextService:GetTextSize(
|
||||
localized.noFriendsText,
|
||||
bodyFontSize,
|
||||
bodyFont,
|
||||
Vector2.new(CONTAINER_WIDTH, math.huge)
|
||||
).Y
|
||||
|
||||
local totalTextPadding = TEXT_PADDING_TOP + TEXT_PADDING_BOTTOM
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, CONTAINER_WIDTH, 1, 0),
|
||||
Position = UDim2.new(0.5, 0, 0, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
|
||||
Icon = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.AddFriend,
|
||||
Size = UDim2.new(0, 64, 0, 64),
|
||||
ImageTransparency = 0.5,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
|
||||
TextContainer = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, textHeight + totalTextPadding),
|
||||
LayoutOrder = 2,
|
||||
}, {
|
||||
Text = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Body",
|
||||
themeKey = "TextEmphasis",
|
||||
Text = localized.noFriendsText,
|
||||
Position = UDim2.new(0, 0, 0, TEXT_PADDING_TOP),
|
||||
Size = UDim2.new(1, 0, 1, -totalTextPadding),
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
TextWrapped = true,
|
||||
})
|
||||
}),
|
||||
|
||||
MakeFriendsButton = Roact.createElement(UIBlox.App.Button.SecondaryButton, {
|
||||
layoutOrder = 3,
|
||||
size = UDim2.new(1, 0, 0, 48),
|
||||
text = localized.makeFriendsNow,
|
||||
onActivated = props.switchToPlayers,
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
nil,
|
||||
function(dispatch)
|
||||
return {
|
||||
switchToPlayers = function()
|
||||
dispatch(SetCurrentPage("Players"))
|
||||
end
|
||||
}
|
||||
end
|
||||
)(AddFriendsNow)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local AddFriendsNow = require(script.Parent.AddFriendsNow)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
AddFriendsNow = Roact.createElement(AddFriendsNow),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local t = InGameMenuDependencies.t
|
||||
local Cryo = InGameMenuDependencies.Cryo
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local FFlagInGameMenuInviteFriendsDisplayNameSearch =
|
||||
require(InGameMenu.Flags.FFlagInGameMenuInviteFriendsDisplayNameSearch)
|
||||
local getFFlagUseNewPlayerLabelDesign = require(InGameMenu.Flags.GetFFlagUseNewPlayerLabelDesign)
|
||||
local fflagUseNewPlayerLabelDesign = getFFlagUseNewPlayerLabelDesign()
|
||||
local PlayerLabel = fflagUseNewPlayerLabelDesign and require(InGameMenu.Components.PlayerLabelV2)
|
||||
or require(InGameMenu.Components.PlayerLabel)
|
||||
|
||||
local Divider = require(InGameMenu.Components.Divider)
|
||||
local SendInviteButton = require(script.Parent.SendInviteButton)
|
||||
local SearchBox = require(script.Parent.SearchBox)
|
||||
local BarOnTopScrollingFrame = require(InGameMenu.Components.BarOnTopScrollingFrame)
|
||||
local PlayerSearchPredicate = require(InGameMenu.Utility.PlayerSearchPredicate)
|
||||
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
local InviteUserToPlaceId = require(InGameMenu.Thunks.InviteUserToPlaceId)
|
||||
|
||||
local DIVIDER_INDENT = fflagUseNewPlayerLabelDesign and 104 or 70
|
||||
local PLAYER_LABEL_HEIGHT = fflagUseNewPlayerLabelDesign and 71 or 70
|
||||
|
||||
local SEARCHBOX_HEIGHT = 36
|
||||
local SEARCHBOX_TOP_PADDING = 8
|
||||
local SEARCHBOX_BOTTOM_PADDING = 20
|
||||
|
||||
local PLACE_ID = tostring(game.PlaceId)
|
||||
|
||||
local InviteFriendsList = Roact.PureComponent:extend("InviteFriendsList")
|
||||
|
||||
InviteFriendsList.validateProps = t.strictInterface({
|
||||
players = t.array(t.strictInterface({
|
||||
IsOnline = t.boolean,
|
||||
Id = t.integer,
|
||||
Username = t.string,
|
||||
DisplayName = t.string,
|
||||
})),
|
||||
invitesState = t.table,
|
||||
dispatchInviteUserToPlaceId = t.callback,
|
||||
})
|
||||
|
||||
local function sortPlayers(p1, p2)
|
||||
if p1.IsOnline == p2.IsOnline then
|
||||
return p1.Username:lower() < p2.Username:lower()
|
||||
else
|
||||
return p1.IsOnline and not p2.IsOnline
|
||||
end
|
||||
end
|
||||
|
||||
local function searchFilter(searchText, playerName, displayName)
|
||||
if searchText == "" then
|
||||
return true
|
||||
end
|
||||
return string.find(playerName:lower(), searchText:lower(), 1, true) ~= nil
|
||||
end
|
||||
|
||||
function InviteFriendsList:init()
|
||||
self.state = {
|
||||
searchText = "",
|
||||
}
|
||||
self.inviteAnimationInProgress = {}
|
||||
end
|
||||
|
||||
function InviteFriendsList:renderListEntries()
|
||||
local sortedPlayers = Cryo.List.sort(self.props.players, sortPlayers)
|
||||
|
||||
local playersCount = #sortedPlayers
|
||||
local layoutOrder = 1
|
||||
|
||||
local listComponents = {}
|
||||
local visibleEntryCount = 0
|
||||
|
||||
listComponents.ListLayout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||
})
|
||||
|
||||
for index, playerInfo in pairs(sortedPlayers) do
|
||||
local userId = tostring(playerInfo.Id)
|
||||
local userInviteStatus = self.props.invitesState[userId]
|
||||
local isEntryVisible
|
||||
if FFlagInGameMenuInviteFriendsDisplayNameSearch then
|
||||
isEntryVisible = PlayerSearchPredicate(self.state.searchText, playerInfo.Username, playerInfo.DisplayName)
|
||||
else
|
||||
isEntryVisible = searchFilter(self.state.searchText, playerInfo.Username, playerInfo.DisplayName)
|
||||
end
|
||||
|
||||
if isEntryVisible then
|
||||
visibleEntryCount = visibleEntryCount + 1
|
||||
end
|
||||
|
||||
listComponents["friend_"..index] = Roact.createElement(PlayerLabel, {
|
||||
username = playerInfo.Username,
|
||||
displayName = fflagUseNewPlayerLabelDesign and playerInfo.DisplayName or nil,
|
||||
userId = playerInfo.Id,
|
||||
isOnline = playerInfo.IsOnline,
|
||||
isSelected = false,
|
||||
LayoutOrder = layoutOrder,
|
||||
Visible = isEntryVisible,
|
||||
|
||||
onActivated = function()
|
||||
local isPending = userInviteStatus == InviteStatus.Pending
|
||||
local isSuccess = userInviteStatus == InviteStatus.Success
|
||||
local inviteAnimationInProgress = self.inviteAnimationInProgress[playerInfo.Id]
|
||||
if not (isPending or isSuccess or inviteAnimationInProgress) then
|
||||
self.props.dispatchInviteUserToPlaceId(userId, PLACE_ID)
|
||||
end
|
||||
end
|
||||
}, {
|
||||
InviteButton = Roact.createElement(SendInviteButton, {
|
||||
userInviteStatus = userInviteStatus,
|
||||
onActivated = function()
|
||||
self.props.dispatchInviteUserToPlaceId(userId, PLACE_ID)
|
||||
end,
|
||||
animationBegun = function()
|
||||
self.inviteAnimationInProgress[playerInfo.Id] = true
|
||||
end,
|
||||
animationEnded = function()
|
||||
self.inviteAnimationInProgress[playerInfo.Id] = nil
|
||||
end,
|
||||
})
|
||||
})
|
||||
|
||||
layoutOrder = layoutOrder + 1
|
||||
|
||||
if index < playersCount then
|
||||
listComponents["divider_" .. layoutOrder] = Roact.createElement(Divider, {
|
||||
LayoutOrder = layoutOrder,
|
||||
Size = UDim2.new(1, -DIVIDER_INDENT, 0, 1),
|
||||
Visible = isEntryVisible,
|
||||
})
|
||||
|
||||
layoutOrder = layoutOrder + 1
|
||||
end
|
||||
end
|
||||
|
||||
return listComponents, visibleEntryCount
|
||||
end
|
||||
|
||||
function InviteFriendsList:render()
|
||||
local listEntries, visibleEntryCount = self:renderListEntries()
|
||||
|
||||
local listOffset = SEARCHBOX_HEIGHT + SEARCHBOX_TOP_PADDING + SEARCHBOX_BOTTOM_PADDING
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
}, {
|
||||
SearchBox = Roact.createElement(SearchBox, {
|
||||
Size = UDim2.new(1, -48, 0, SEARCHBOX_HEIGHT),
|
||||
Position = UDim2.new(0.5, 0, 0, SEARCHBOX_TOP_PADDING),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
searchUpdated = function(newSearch)
|
||||
self:setState({
|
||||
searchText = newSearch,
|
||||
})
|
||||
end,
|
||||
}),
|
||||
|
||||
List = Roact.createElement(BarOnTopScrollingFrame, {
|
||||
Position = UDim2.new(0, 0, 0, listOffset),
|
||||
Size = UDim2.new(1, 0, 1, -listOffset),
|
||||
CanvasSize = UDim2.new(1, 0, 0, visibleEntryCount * (PLAYER_LABEL_HEIGHT + 1)),
|
||||
scrollBarOffset = 4,
|
||||
}, listEntries)
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
invitesState = state.invites
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
dispatchInviteUserToPlaceId = function(userId, placeId)
|
||||
dispatch(InviteUserToPlaceId(userId, placeId))
|
||||
end
|
||||
}
|
||||
end
|
||||
)(InviteFriendsList)
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local ReceivedUserInviteStatus = require(InGameMenu.Actions.ReceivedUserInviteStatus)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
local InviteFriendsList = require(script.Parent.InviteFriendsList)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
InviteFriendsList = Roact.createElement(InviteFriendsList, {
|
||||
players = {
|
||||
{
|
||||
Id = 2231221,
|
||||
IsOnline = true,
|
||||
Username = "TheGamer101",
|
||||
DisplayName = "TestDisplayName",
|
||||
},
|
||||
{
|
||||
Id = 261,
|
||||
IsOnline = false,
|
||||
Username = "Shedletsky",
|
||||
DisplayName = "TestDisplayName",
|
||||
},
|
||||
}
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("should not throw erros when invite state is updated", function()
|
||||
local store = Rodux.Store.new(reducer)
|
||||
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = store
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
InviteFriendsList = Roact.createElement(InviteFriendsList, {
|
||||
players = {
|
||||
{
|
||||
Id = 2231221,
|
||||
IsOnline = true,
|
||||
Username = "TheGamer101",
|
||||
DisplayName = "TestDisplayName",
|
||||
},
|
||||
{
|
||||
Id = 261,
|
||||
IsOnline = false,
|
||||
Username = "Shedletsky",
|
||||
DisplayName = "TestDisplayName",
|
||||
},
|
||||
}
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
|
||||
store:dispatch(ReceivedUserInviteStatus(261, InviteStatus.Failed))
|
||||
store:flush()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local TextService = game:GetService("TextService")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local CONTAINER_WIDTH = 304
|
||||
local TEXT_PADDING_TOP = 10
|
||||
local TEXT_PADDING_BOTTOM = 26
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
onRetry = t.callback,
|
||||
})
|
||||
|
||||
local function LoadingFriendsError(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withStyle(function(style)
|
||||
return withLocalization({
|
||||
errorOccured = "CoreScripts.InGameMenu.InviteFriends.ErrorOccured",
|
||||
})(function(localized)
|
||||
local bodyFont = style.Font.Body.Font
|
||||
local bodyFontSize = style.Font.Body.RelativeSize * style.Font.BaseSize
|
||||
local textHeight = TextService:GetTextSize(
|
||||
localized.errorOccured,
|
||||
bodyFontSize,
|
||||
bodyFont,
|
||||
Vector2.new(CONTAINER_WIDTH, math.huge)
|
||||
).Y
|
||||
|
||||
local totalTextPadding = TEXT_PADDING_TOP + TEXT_PADDING_BOTTOM
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, CONTAINER_WIDTH, 1, 0),
|
||||
Position = UDim2.new(0.5, 0, 0, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
|
||||
Icon = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.Friends,
|
||||
Size = UDim2.new(0, 64, 0, 64),
|
||||
ImageTransparency = 0.5,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
|
||||
TextContainer = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, textHeight + totalTextPadding),
|
||||
LayoutOrder = 2,
|
||||
}, {
|
||||
Text = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = "Body",
|
||||
themeKey = "TextEmphasis",
|
||||
Text = localized.errorOccured,
|
||||
Position = UDim2.new(0, 0, 0, TEXT_PADDING_TOP),
|
||||
Size = UDim2.new(1, 0, 1, -totalTextPadding),
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
TextWrapped = true,
|
||||
})
|
||||
}),
|
||||
|
||||
MakeFriendsButton = Roact.createElement(UIBlox.App.Button.SecondaryButton, {
|
||||
layoutOrder = 3,
|
||||
size = UDim2.new(1, 0, 0, 48),
|
||||
icon = Assets.Images.RetryIcon,
|
||||
onActivated = props.onRetry,
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
return LoadingFriendsError
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local Rodux = InGameMenuDependencies.Rodux
|
||||
local RoactRodux = InGameMenuDependencies.RoactRodux
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
local Localization = require(InGameMenu.Localization.Localization)
|
||||
local LocalizationProvider = require(InGameMenu.Localization.LocalizationProvider)
|
||||
local reducer = require(InGameMenu.reducer)
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local LoadingFriendsError = require(script.Parent.LoadingFriendsError)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
|
||||
local element = Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = Rodux.Store.new(reducer)
|
||||
}, {
|
||||
ThemeProvider = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
LocalizationProvider = Roact.createElement(LocalizationProvider, {
|
||||
localization = Localization.new("en-us"),
|
||||
}, {
|
||||
LoadingFriendsError = Roact.createElement(LoadingFriendsError, {
|
||||
onRetry = function()
|
||||
print("On retry")
|
||||
end,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local TEXT_BOX_OFFSET = 44
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
local ImageSetButton = UIBlox.Core.ImageSet.Button
|
||||
|
||||
local SearchBox = Roact.PureComponent:extend("SearchBox")
|
||||
|
||||
SearchBox.validateProps = t.strictInterface({
|
||||
Size = t.UDim2,
|
||||
Position = t.UDim2,
|
||||
AnchorPoint = t.Vector2,
|
||||
searchUpdated = t.callback,
|
||||
})
|
||||
|
||||
function SearchBox:init()
|
||||
self.state = {
|
||||
searchText = "",
|
||||
}
|
||||
end
|
||||
|
||||
function SearchBox:render()
|
||||
return withLocalization({
|
||||
searchPlaceholder = "CoreScripts.InGameMenu.InviteFriends.Search",
|
||||
})(function(localized)
|
||||
return withStyle(function(style)
|
||||
local textTheme = style.Theme.TextEmphasis
|
||||
local textFont = style.Font.Body
|
||||
|
||||
return Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Size = self.props.Size,
|
||||
Position = self.props.Position,
|
||||
AnchorPoint = self.props.AnchorPoint,
|
||||
|
||||
Image = Assets.Images.RoundedRect.Image,
|
||||
ImageColor3 = style.Theme.BackgroundUIContrast.Color,
|
||||
ImageTransparency = style.Theme.BackgroundUIContrast.Transparency,
|
||||
ScaleType = Assets.Images.RoundedRect.ScaleType,
|
||||
SliceCenter = Assets.Images.RoundedRect.SliceCenter,
|
||||
}, {
|
||||
Border = Roact.createElement(ImageSetLabel, {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.RoundedRectBorder.Image,
|
||||
SliceCenter = Assets.Images.RoundedRectBorder.SliceCenter,
|
||||
ScaleType = Assets.Images.RoundedRectBorder.ScaleType,
|
||||
ImageColor3 = style.Theme.Divider.Color,
|
||||
ImageTransparency = style.Theme.Divider.Transparency,
|
||||
}),
|
||||
|
||||
SearchIcon = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, 16, 0, 16),
|
||||
Position = UDim2.new(0, 22, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Image = Assets.Images.SearchIcon,
|
||||
ImageColor3 = style.Theme.IconEmphasis.Color,
|
||||
}),
|
||||
|
||||
TextBox = Roact.createElement("TextBox", {
|
||||
Size = UDim2.new(1, -TEXT_BOX_OFFSET, 1, 0),
|
||||
Position = UDim2.new(0, TEXT_BOX_OFFSET, 0, 0),
|
||||
PlaceholderText = "",
|
||||
Text = self.state.searchText,
|
||||
ClearTextOnFocus = false,
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
|
||||
TextColor3 = textTheme.Color,
|
||||
TextTransparency = textTheme.Transparency,
|
||||
Font = textFont.Font,
|
||||
TextSize = textFont.RelativeSize * style.Font.BaseSize,
|
||||
TextWrapped = false,
|
||||
|
||||
[Roact.Change.Text] = function(rbx)
|
||||
if self.state.searchText ~= rbx.Text then
|
||||
self:setState({
|
||||
searchText = rbx.Text,
|
||||
})
|
||||
self.props.searchUpdated(self.state.searchText)
|
||||
end
|
||||
end,
|
||||
}),
|
||||
|
||||
--TextBox.PlaceholderText can't be used as it's text transparency can't be changed.
|
||||
PlaceholderText = Roact.createElement(ThemedTextLabel, {
|
||||
themeKey = "TextEmphasis",
|
||||
fontKey = "Body",
|
||||
Text = localized.searchPlaceholder,
|
||||
Size = UDim2.new(1, -TEXT_BOX_OFFSET, 1, 0),
|
||||
Position = UDim2.new(0, TEXT_BOX_OFFSET, 0, 0),
|
||||
TextTransparency = 0.5,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
ZIndex = 2,
|
||||
Visible = self.state.searchText == "",
|
||||
}),
|
||||
|
||||
ClearButton = Roact.createElement(ImageSetButton, {
|
||||
Size = UDim2.new(0, 18, 0, 18),
|
||||
Position = UDim2.new(1, -14, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(1, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
Image = Assets.Images.ClearIcon,
|
||||
ImageColor3 = style.Theme.IconEmphasis.Color,
|
||||
Visible = self.state.searchText ~= "",
|
||||
[Roact.Event.Activated] = function()
|
||||
self:setState({
|
||||
searchText = "",
|
||||
})
|
||||
self.props.searchUpdated("")
|
||||
end,
|
||||
}),
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
return SearchBox
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
local RunService = game:GetService("RunService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
local Images = UIBlox.App.ImageSet.Images
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
local FFlagFixMenuIcons = require(InGameMenu.Flags.FFlagFixMenuIcons)
|
||||
|
||||
local SendInviteButton = Roact.PureComponent:extend("SendInviteButton")
|
||||
|
||||
SendInviteButton.validateProps = t.strictInterface({
|
||||
onActivated = t.callback,
|
||||
animationBegun = t.callback,
|
||||
animationEnded = t.callback,
|
||||
userInviteStatus = t.optional(t.string)
|
||||
})
|
||||
|
||||
local FPS = 30
|
||||
|
||||
-- Returns 0-1 based on the value of time betweenStartFrame and endFrame
|
||||
local function linearTween(dTime, startFrame, endFrame)
|
||||
local range = (endFrame - startFrame)/FPS
|
||||
return (dTime - (startFrame/FPS)) / range
|
||||
end
|
||||
|
||||
local function isBetweenFrames(dTime, startFrame, endFrame)
|
||||
return dTime >= (startFrame/FPS) and dTime <= (endFrame/FPS)
|
||||
end
|
||||
|
||||
local STATUS_TO_ANIMATION_MAP = {
|
||||
[InviteStatus.Pending] = function(timeElapsed, updateBindings)
|
||||
local maxTransparency = 0.75
|
||||
local minTransparency = 0
|
||||
local range = (maxTransparency - minTransparency) / 2
|
||||
|
||||
-- Tween transparency on a sin wave for a transparency "pulse" effect
|
||||
local transparency = range + (range * math.sin(2 * math.pi * timeElapsed))
|
||||
updateBindings.sendTransparency(transparency)
|
||||
end,
|
||||
[InviteStatus.Success] = function(timeElapsed, updateBindings, stopCallback)
|
||||
-- Fade out send icon, fade-in and scale-down success icon
|
||||
if isBetweenFrames(timeElapsed, 0, 4) then
|
||||
local scaledElapse = linearTween(timeElapsed, 0, 4)
|
||||
updateBindings.sendTransparency(scaledElapse)
|
||||
updateBindings.successTransparency(1-scaledElapse)
|
||||
|
||||
local successSizeMax = 1.5
|
||||
local successSizeMin = 0.75
|
||||
local range = successSizeMax-successSizeMin
|
||||
local size = successSizeMin + (range * (1 - scaledElapse))
|
||||
updateBindings.successSize(size)
|
||||
|
||||
-- Complete fade-outs, scale success icon to normal size
|
||||
elseif isBetweenFrames(timeElapsed, 4, 9) then
|
||||
updateBindings.sendTransparency(1)
|
||||
updateBindings.successTransparency(0)
|
||||
|
||||
local scaledElapse = linearTween(timeElapsed, 4, 9)
|
||||
local successSizeMax = 1
|
||||
local successSizeMin = 0.75
|
||||
local range = successSizeMax-successSizeMin
|
||||
local size = successSizeMin + (range * (scaledElapse))
|
||||
updateBindings.successSize(size)
|
||||
|
||||
-- Complete success icon size tween,
|
||||
else
|
||||
updateBindings.successSize(1)
|
||||
stopCallback()
|
||||
end
|
||||
end,
|
||||
[InviteStatus.Failed] = function(timeElapsed, updateBindings, stopCallback)
|
||||
-- Fade out send icon
|
||||
if isBetweenFrames(timeElapsed, 0, 2) then
|
||||
local scaledElapse = linearTween(timeElapsed, 0, 4)
|
||||
updateBindings.sendTransparency(scaledElapse)
|
||||
|
||||
-- Hold fadeout to frame 52
|
||||
elseif isBetweenFrames(timeElapsed, 2, 52) then
|
||||
updateBindings.sendTransparency(1)
|
||||
|
||||
-- Fade in send icon
|
||||
elseif isBetweenFrames(timeElapsed, 52, 57) then
|
||||
local scaledElapse = linearTween(timeElapsed, 52, 57)
|
||||
updateBindings.sendTransparency(1-scaledElapse)
|
||||
|
||||
-- Finish send fade-in and stop animation
|
||||
else
|
||||
updateBindings.sendTransparency(0)
|
||||
stopCallback()
|
||||
end
|
||||
|
||||
-- Fade in fail icon
|
||||
if isBetweenFrames(timeElapsed, 0, 4) then
|
||||
local scaledElapse = linearTween(timeElapsed, 0, 4)
|
||||
updateBindings.failTransparency(1-scaledElapse)
|
||||
|
||||
-- Shake fail icon
|
||||
elseif isBetweenFrames(timeElapsed, 4, 42) then
|
||||
updateBindings.failTransparency(0)
|
||||
|
||||
local e = 2.71828182846
|
||||
-- Shake amplitude
|
||||
local a = -0.4
|
||||
-- Shake frequency
|
||||
local f = 40
|
||||
-- Shake damping
|
||||
local k = 8
|
||||
|
||||
-- Damped oscillation https://www.desmos.com/calculator/ajx32emroq
|
||||
local et = timeElapsed - 4/FPS
|
||||
local pos = a * e^(-et * k) * math.sin(et * f)
|
||||
updateBindings.failPos(pos)
|
||||
|
||||
-- Fadeout fail icon
|
||||
elseif isBetweenFrames(timeElapsed, 42, 47) then
|
||||
updateBindings.failPos(0)
|
||||
|
||||
local scaledElapse = linearTween(timeElapsed, 42, 47)
|
||||
updateBindings.failTransparency(scaledElapse)
|
||||
|
||||
-- Complete fadeout
|
||||
else
|
||||
updateBindings.failTransparency(1)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
function SendInviteButton:init(initialProps)
|
||||
local sendTransparency, updateSendTransparency = Roact.createBinding(0)
|
||||
local successTransparency, updateSuccessTransparency = Roact.createBinding(1)
|
||||
local failTransparency, updateFailTransparency = Roact.createBinding(1)
|
||||
|
||||
local successSize, updateSuccessSize = Roact.createBinding(1)
|
||||
local failPos, updateFailPos = Roact.createBinding(0)
|
||||
|
||||
self.bindings = {
|
||||
sendTransparency = sendTransparency,
|
||||
successTransparency = successTransparency,
|
||||
failTransparency = failTransparency,
|
||||
successSize = successSize:map(function(size)
|
||||
return UDim2.new(size, 0, size, 0)
|
||||
end),
|
||||
failPos = failPos:map(function(pos)
|
||||
return UDim2.new(pos, 0, 0, 0)
|
||||
end),
|
||||
}
|
||||
|
||||
self.updateBindings = {
|
||||
sendTransparency = updateSendTransparency,
|
||||
successTransparency = updateSuccessTransparency,
|
||||
failTransparency = updateFailTransparency,
|
||||
successSize = updateSuccessSize,
|
||||
failPos = updateFailPos,
|
||||
}
|
||||
|
||||
if initialProps.userInviteStatus == InviteStatus.Success then
|
||||
updateSuccessTransparency(0)
|
||||
updateSendTransparency(1)
|
||||
end
|
||||
end
|
||||
|
||||
function SendInviteButton:render()
|
||||
local props = self.props
|
||||
|
||||
return withStyle(function(style)
|
||||
return Roact.createElement("TextButton", {
|
||||
Size = UDim2.new(0, 36, 0, 36),
|
||||
BackgroundTransparency = 1,
|
||||
Text = "",
|
||||
|
||||
[Roact.Event.Activated] = function()
|
||||
local isPending = props.userInviteStatus == InviteStatus.Pending
|
||||
local isSuccess = props.userInviteStatus == InviteStatus.Success
|
||||
if not self.renderSteppedConnection and not (isPending or isSuccess) then
|
||||
props.onActivated()
|
||||
end
|
||||
end
|
||||
}, {
|
||||
SendLabel = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = FFlagFixMenuIcons and Images["icons/actions/friends/friendInvite"] or Assets.Images.SendInvite,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
ImageColor3 = style.Theme.IconEmphasis.Color,
|
||||
|
||||
ImageTransparency = self.bindings.sendTransparency,
|
||||
}),
|
||||
SuccessLabel = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = FFlagFixMenuIcons and Images["icons/status/success"] or Assets.Images.InviteSuccess,
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
ImageColor3 = style.Theme.IconEmphasis.Color,
|
||||
|
||||
ImageTransparency = self.bindings.successTransparency,
|
||||
Size = self.bindings.successSize
|
||||
}),
|
||||
FailLabel = Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Image = FFlagFixMenuIcons and Images["icons/status/alert"] or Assets.Images.InviteFail,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
ImageColor3 = style.Theme.IconEmphasis.Color,
|
||||
|
||||
ImageTransparency = self.bindings.failTransparency,
|
||||
Position = self.bindings.failPos
|
||||
})
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
function SendInviteButton:didUpdate(previousProps)
|
||||
if self.props.userInviteStatus and self.props.userInviteStatus ~= previousProps.userInviteStatus then
|
||||
self:playAnimation(STATUS_TO_ANIMATION_MAP[self.props.userInviteStatus])
|
||||
end
|
||||
end
|
||||
|
||||
function SendInviteButton:didMount()
|
||||
if self.props.userInviteStatus then
|
||||
self:playAnimation(STATUS_TO_ANIMATION_MAP[self.props.userInviteStatus])
|
||||
end
|
||||
end
|
||||
|
||||
function SendInviteButton:playAnimation(animFunc)
|
||||
self.animationStartTime = tick()
|
||||
self.animationFunction = animFunc
|
||||
|
||||
if not self.renderSteppedConnection then
|
||||
self.renderSteppedConnection = RunService.RenderStepped:Connect(function()
|
||||
self.animationFunction(tick() - self.animationStartTime, self.updateBindings, function()
|
||||
self:stopAnimation()
|
||||
end)
|
||||
end)
|
||||
self.props.animationBegun()
|
||||
end
|
||||
end
|
||||
|
||||
function SendInviteButton:stopAnimation()
|
||||
if self.renderSteppedConnection then
|
||||
self.renderSteppedConnection:Disconnect()
|
||||
self.renderSteppedConnection = nil
|
||||
self.props.animationEnded()
|
||||
end
|
||||
end
|
||||
|
||||
function SendInviteButton:willUnmount()
|
||||
self:stopAnimation()
|
||||
end
|
||||
|
||||
return SendInviteButton
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
|
||||
local InGameMenu = script.Parent.Parent.Parent
|
||||
|
||||
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
|
||||
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
|
||||
|
||||
local appStyle = {
|
||||
Theme = AppDarkTheme,
|
||||
Font = AppFont,
|
||||
}
|
||||
|
||||
local SendInviteButton = require(script.Parent.SendInviteButton)
|
||||
|
||||
local Constants = require(InGameMenu.Resources.Constants)
|
||||
local InviteStatus = Constants.InviteStatus
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
SendInviteButton = Roact.createElement(SendInviteButton, {
|
||||
onActivated = function()
|
||||
print("onActivated")
|
||||
end,
|
||||
animationBegun = function()
|
||||
print("animationBegun")
|
||||
end,
|
||||
animationEnded = function()
|
||||
print("animationEnded")
|
||||
end,
|
||||
userInviteStatus = nil,
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
describe("animations", function()
|
||||
it("should call animation began and ended once", function()
|
||||
local animationBegunCalled = 0
|
||||
local animationEndedCalled = 0
|
||||
|
||||
local element = Roact.createElement(UIBlox.Core.Style.Provider, {
|
||||
style = appStyle,
|
||||
}, {
|
||||
SendInviteButton = Roact.createElement(SendInviteButton, {
|
||||
onActivated = function()
|
||||
print("onActivated")
|
||||
end,
|
||||
animationBegun = function()
|
||||
animationBegunCalled = animationBegunCalled + 1
|
||||
end,
|
||||
animationEnded = function()
|
||||
animationEndedCalled = animationEndedCalled + 1
|
||||
end,
|
||||
userInviteStatus = InviteStatus.Failed,
|
||||
}),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
wait(1)
|
||||
Roact.unmount(instance)
|
||||
|
||||
expect(animationBegunCalled).to.equal(1)
|
||||
expect(animationEndedCalled).to.equal(1)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
local Players = game:GetService("Players")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local Promise = require(InGameMenu.Utility.Promise)
|
||||
local withLocalization = require(InGameMenu.Localization.withLocalization)
|
||||
|
||||
local Page = require(InGameMenu.Components.Page)
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
local PageNavigationWatcher = require(InGameMenu.Components.PageNavigationWatcher)
|
||||
local InviteFriendsList = require(script.InviteFriendsList)
|
||||
local AddFriendsNow = require(script.AddFriendsNow)
|
||||
local LoadingFriendsError = require(script.LoadingFriendsError)
|
||||
|
||||
local InviteFriendsPage = Roact.PureComponent:extend("InviteFriendsPage")
|
||||
InviteFriendsPage.validateProps = t.strictInterface({
|
||||
pageTitle = t.string,
|
||||
})
|
||||
|
||||
function InviteFriendsPage:init()
|
||||
self.state = {
|
||||
loadingFriends = true,
|
||||
loadingFriendsError = false,
|
||||
friends = {},
|
||||
}
|
||||
end
|
||||
|
||||
function InviteFriendsPage:renderLoadingPage()
|
||||
return withLocalization({
|
||||
loading = "CoreScripts.InGameMenu.InviteFriends.Loading",
|
||||
})(function(localized)
|
||||
return Roact.createElement(ThemedTextLabel, {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Text = localized.loading,
|
||||
|
||||
fontKey = "Header1",
|
||||
themeKey = "TextEmphasis",
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
function InviteFriendsPage:renderError()
|
||||
return Roact.createElement(LoadingFriendsError, {
|
||||
onRetry = function()
|
||||
self:setState({
|
||||
loadingFriends = true,
|
||||
loadingFriendsError = false,
|
||||
})
|
||||
self:loadFriends()
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function InviteFriendsPage:renderNoFriends()
|
||||
return Roact.createElement(AddFriendsNow)
|
||||
end
|
||||
|
||||
function InviteFriendsPage:renderFriends()
|
||||
return Roact.createElement(InviteFriendsList, {
|
||||
players = self.state.friends,
|
||||
})
|
||||
end
|
||||
|
||||
function InviteFriendsPage:render()
|
||||
local state = self.state
|
||||
local children = {
|
||||
Watcher = Roact.createElement(PageNavigationWatcher, {
|
||||
desiredPage = "InviteFriends",
|
||||
onNavigateTo = function()
|
||||
self:loadFriends()
|
||||
end,
|
||||
})
|
||||
}
|
||||
|
||||
if state.loadingFriends then
|
||||
children.Loading = self:renderLoadingPage()
|
||||
elseif state.loadingFriendsError then
|
||||
children.ErrorLoading = self:renderError()
|
||||
elseif #state.friends == 0 then
|
||||
children.NoFriends = self:renderNoFriends()
|
||||
else
|
||||
children.FriendsList = self:renderFriends()
|
||||
end
|
||||
|
||||
return Roact.createElement(Page, {
|
||||
pageTitle = self.props.pageTitle,
|
||||
}, children)
|
||||
end
|
||||
|
||||
function InviteFriendsPage:didMount()
|
||||
self.mounted = true
|
||||
self:loadFriends()
|
||||
end
|
||||
|
||||
function InviteFriendsPage:loadFriends()
|
||||
Promise.new(function(resolve, reject)
|
||||
coroutine.wrap(function()
|
||||
local localPlayer = Players.LocalPlayer
|
||||
local success, friendsPages = pcall(function()
|
||||
return Players:GetFriendsAsync(localPlayer.UserId)
|
||||
end)
|
||||
|
||||
if not success then
|
||||
reject("Error loading friends")
|
||||
return
|
||||
end
|
||||
|
||||
local friends = {}
|
||||
|
||||
while true do
|
||||
for _, item in ipairs(friendsPages:GetCurrentPage()) do
|
||||
friends[#friends+1] = {
|
||||
IsOnline = item.IsOnline,
|
||||
Id = item.Id,
|
||||
Username = item.Username,
|
||||
DisplayName = item.DisplayName,
|
||||
}
|
||||
end
|
||||
|
||||
if not friendsPages.IsFinished then
|
||||
success = pcall(function()
|
||||
friendsPages:AdvanceToNextPageAsync()
|
||||
end)
|
||||
if not success then
|
||||
reject("Error loading friends")
|
||||
return
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
resolve(friends)
|
||||
end)()
|
||||
end):andThen(function(friends)
|
||||
if self.mounted then
|
||||
self:setState({
|
||||
loadingFriends = false,
|
||||
friends = friends
|
||||
})
|
||||
end
|
||||
end):catch(function()
|
||||
if self.mounted then
|
||||
self:setState({
|
||||
loadingFriends = false,
|
||||
loadingFriendsError = true,
|
||||
})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function InviteFriendsPage:willUnmount()
|
||||
self.mounted = false
|
||||
end
|
||||
|
||||
return InviteFriendsPage
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
local TextService = game:GetService("TextService")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local InGameMenuDependencies = require(CorePackages.InGameMenuDependencies)
|
||||
local Roact = InGameMenuDependencies.Roact
|
||||
local UIBlox = InGameMenuDependencies.UIBlox
|
||||
local t = InGameMenuDependencies.t
|
||||
|
||||
local withStyle = UIBlox.Core.Style.withStyle
|
||||
|
||||
local InGameMenu = script.Parent.Parent
|
||||
|
||||
local Assets = require(InGameMenu.Resources.Assets)
|
||||
|
||||
local GlobalConfig = require(InGameMenu.GlobalConfig)
|
||||
|
||||
local ThemedTextLabel = require(InGameMenu.Components.ThemedTextLabel)
|
||||
|
||||
local ImageSetLabel = UIBlox.Core.ImageSet.Label
|
||||
|
||||
-- Additional width for keys with centered text like [Backspace], [Enter], etc
|
||||
local CENTERED_EXTRA_WIDTH = 14 * 2
|
||||
|
||||
-- Side padding for off-centered keys [Shift ]
|
||||
local OFF_CENTER_PADDING = 9
|
||||
|
||||
-- Big and small font sizes for key text. (Var names are succinct to make
|
||||
-- populating override more easy)
|
||||
local BIG = "CaptionHeader"
|
||||
local SMALL = "Footer"
|
||||
|
||||
local DEFAULT_BORDER_THEME = "UIEmphasis"
|
||||
local DEFAULT_TEXT_THEME = "TextEmphasis"
|
||||
|
||||
local CONTENT_OVERRIDE_MAP = {
|
||||
[Enum.KeyCode.Escape] = {text = "ESC", fontKey = SMALL, width = 36},
|
||||
[Enum.KeyCode.Space] = {text = "Space", width = 92},
|
||||
[Enum.KeyCode.LeftShift] = {text = "Shift", width = 66, alignment = Enum.TextXAlignment.Left},
|
||||
[Enum.KeyCode.LeftControl] = {text = "Ctrl", width = 66, alignment = Enum.TextXAlignment.Left},
|
||||
|
||||
[Enum.KeyCode.LeftSuper] = {text = "Command"},
|
||||
[Enum.KeyCode.LeftMeta] = {text = "fn"},
|
||||
[Enum.KeyCode.LeftAlt] = {text = "Opt"},
|
||||
|
||||
[Enum.KeyCode.Tilde] = {text = "~", fontKey = BIG},
|
||||
|
||||
[Enum.KeyCode.F10] = {fontKey = BIG, width = 36},
|
||||
[Enum.KeyCode.F11] = {fontKey = BIG, width = 36},
|
||||
|
||||
[Enum.KeyCode.Up] = {image = Assets.Images.UpArrow},
|
||||
[Enum.KeyCode.Down] = {image = Assets.Images.DownArrow},
|
||||
[Enum.KeyCode.Left] = {image = Assets.Images.LeftArrow},
|
||||
[Enum.KeyCode.Right] = {image = Assets.Images.RightArrow},
|
||||
}
|
||||
|
||||
local validateProps = t.strictInterface({
|
||||
input = t.enum(Enum.KeyCode),
|
||||
--Defaults to UIEmphasis
|
||||
borderThemeKey = t.optional(t.string),
|
||||
--Defaults to TextEmphasis
|
||||
textThemeKey = t.optional(t.string),
|
||||
|
||||
AnchorPoint = t.optional(t.Vector2),
|
||||
Position = t.optional(t.UDim2),
|
||||
LayoutOrder = t.optional(t.integer),
|
||||
ZIndex = t.optional(t.integer),
|
||||
})
|
||||
|
||||
local function getLabelWidthAndContent(input, style, textThemeKey)
|
||||
local override = CONTENT_OVERRIDE_MAP[input]
|
||||
|
||||
if override and override.image then
|
||||
local width = 36
|
||||
local content = Roact.createElement(ImageSetLabel, {
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
Image = override.image,
|
||||
ImageColor3 = style.Theme.IconEmphasis.Color,
|
||||
ImageTransparency = style.Theme.IconEmphasis.Transparency,
|
||||
Position = UDim2.new(0.5, 0, 0.5, -1),
|
||||
Size = UDim2.new(0, 16, 0, 16),
|
||||
})
|
||||
|
||||
return width, content
|
||||
else
|
||||
local text, fontKey, width, alignment do
|
||||
local keyString = UserInputService:GetStringForKeyCode(input)
|
||||
|
||||
if override and override.text then
|
||||
text = override.text
|
||||
elseif keyString and keyString ~= "" then
|
||||
text = keyString
|
||||
else
|
||||
text = input.Name
|
||||
end
|
||||
|
||||
local textIsShort = text:len() < 3
|
||||
|
||||
if override and override.fontKey then
|
||||
fontKey = override.fontKey
|
||||
else
|
||||
fontKey = textIsShort and BIG or SMALL
|
||||
end
|
||||
|
||||
if override and override.width then
|
||||
width = override.width
|
||||
elseif textIsShort then
|
||||
width = 36
|
||||
else
|
||||
local fontStyle = style.Font[fontKey]
|
||||
|
||||
local textSize = fontStyle.RelativeSize * style.Font.BaseSize
|
||||
local fontType = fontStyle.Font
|
||||
|
||||
local textWidth = TextService:GetTextSize(
|
||||
text,
|
||||
textSize,
|
||||
fontType,
|
||||
Vector2.new(math.huge, 36)
|
||||
).X
|
||||
|
||||
width = textWidth + CENTERED_EXTRA_WIDTH
|
||||
end
|
||||
|
||||
if override and override.alignment then
|
||||
alignment = override.alignment
|
||||
end
|
||||
end
|
||||
|
||||
local content = Roact.createElement(ThemedTextLabel, {
|
||||
fontKey = fontKey,
|
||||
themeKey = textThemeKey or DEFAULT_TEXT_THEME,
|
||||
Text = text,
|
||||
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Position = UDim2.new(0, 0, 0, -1),
|
||||
TextXAlignment = alignment,
|
||||
})
|
||||
|
||||
return width, content, alignment
|
||||
end
|
||||
end
|
||||
|
||||
local function KeyLabel(props)
|
||||
if GlobalConfig.propValidation then
|
||||
assert(validateProps(props))
|
||||
end
|
||||
|
||||
return withStyle(function(style)
|
||||
local borderTheme = style.Theme[props.borderThemeKey or DEFAULT_BORDER_THEME]
|
||||
|
||||
local width, content, alignment = getLabelWidthAndContent(
|
||||
props.input,
|
||||
style,
|
||||
props.textThemeKey
|
||||
)
|
||||
|
||||
local padding
|
||||
if alignment then
|
||||
padding = OFF_CENTER_PADDING
|
||||
end
|
||||
|
||||
return Roact.createElement(ImageSetLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
ImageTransparency = borderTheme.Transparency,
|
||||
ImageColor3 = borderTheme.Color,
|
||||
|
||||
Image = Assets.Images.KeyBorder,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(Vector2.new(9, 7), Vector2.new(26, 26)),
|
||||
|
||||
Size = UDim2.new(0, width, 0, 36),
|
||||
Position = props.Position,
|
||||
AnchorPoint = props.AnchorPoint,
|
||||
|
||||
LayoutOrder = props.LayoutOrder,
|
||||
ZIndex = props.ZIndex,
|
||||
}, {
|
||||
Padding = padding and Roact.createElement("UIPadding", {
|
||||
PaddingLeft = UDim.new(0, padding),
|
||||
PaddingRight = UDim.new(0, padding)
|
||||
}),
|
||||
LabelContent = content
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
return KeyLabel
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user