add gs
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"lint": {
|
||||
"SameLineStatement": "fatal",
|
||||
"LocalShadow": "fatal",
|
||||
"LocalUnused": "fatal",
|
||||
"FunctionUnused": "fatal",
|
||||
"ImportUnused": "fatal"
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"lint": {
|
||||
"ImplicitReturn": "fatal"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function()
|
||||
return {}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(assets)
|
||||
return {
|
||||
assets = assets,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(bundles)
|
||||
return {
|
||||
bundles = bundles,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(assetId, bundleIds)
|
||||
return {
|
||||
assetId = assetId,
|
||||
bundleIds = bundleIds,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(viewingDetails, assetId)
|
||||
return {
|
||||
viewingDetails = viewingDetails,
|
||||
assetId = assetId,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(equippedAssets)
|
||||
return {
|
||||
equippedAssets = equippedAssets,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(id, isFavorite)
|
||||
return {
|
||||
id = id,
|
||||
isFavorite = isFavorite,
|
||||
}
|
||||
end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(id, isFavorite)
|
||||
return {
|
||||
id = id,
|
||||
isFavorite = isFavorite,
|
||||
}
|
||||
end)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(gamepadEnabled)
|
||||
return {
|
||||
gamepadEnabled = gamepadEnabled,
|
||||
}end)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(itemId, itemType)
|
||||
return {
|
||||
itemId = itemId,
|
||||
itemType = itemType,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(locale)
|
||||
return {
|
||||
locale = locale,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(playerId)
|
||||
return {
|
||||
playerId = playerId,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(playerName)
|
||||
return {
|
||||
playerName = playerName,
|
||||
}
|
||||
end)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(tryingOn, assetId, assetTypeId)
|
||||
return {
|
||||
tryingOn = tryingOn,
|
||||
assetId = assetId,
|
||||
assetTypeId = assetTypeId,
|
||||
}
|
||||
end)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function(view)
|
||||
return {
|
||||
view = view,
|
||||
}
|
||||
end)
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function()
|
||||
return {}
|
||||
end)
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Action = require(CorePackages.AppTempCommon.Common.Action)
|
||||
|
||||
return Action(script.Name, function()
|
||||
return {}
|
||||
end)
|
||||
@@ -0,0 +1,34 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Color = require(CorePackages.AppTempCommon.Common.Color)
|
||||
|
||||
local Colors = {
|
||||
Obsidian = Color.Color3FromHex(0x18191B),
|
||||
Carbon = Color.Color3FromHex(0x1F2123),
|
||||
Slate = Color.Color3FromHex(0x232527),
|
||||
Flint = Color.Color3FromHex(0x393B3D),
|
||||
Graphite = Color.Color3FromHex(0x656668),
|
||||
Pumice = Color.Color3FromHex(0xBDBEBE),
|
||||
|
||||
Black = Color3.fromRGB(0, 0, 0),
|
||||
White = Color3.fromRGB(255, 255, 255),
|
||||
|
||||
Gray1 = Color.Color3FromHex(0x191919),
|
||||
Gray2 = Color.Color3FromHex(0x757575),
|
||||
Gray4 = Color.Color3FromHex(0xE3E3E3),
|
||||
|
||||
GrayBackground = Color.Color3FromHex(0xD8D8D8),
|
||||
Green = Color.Color3FromHex(0x00B06F),
|
||||
DisabledGreen = Color.Color3FromHex(0x9CC4A4),
|
||||
}
|
||||
|
||||
setmetatable(Colors,
|
||||
{
|
||||
__newindex = function(t, key, index)
|
||||
end,
|
||||
__index = function(t, index)
|
||||
error("Colors table has no value: " .. tostring(index))
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
return Colors
|
||||
@@ -0,0 +1,46 @@
|
||||
local Colors = require(script.Parent.Colors)
|
||||
|
||||
local CompactView = {
|
||||
ContainerBackgroundTransparency = 0,
|
||||
ContainerBackgroundColor = Colors.Carbon,
|
||||
UseContainerAspectRatio = false,
|
||||
ContainerAspectRatio = nil,
|
||||
MaxAssetCardsPerRow = 3,
|
||||
ContainerSize = UDim2.new(1, 0, 1, -36),
|
||||
ContainerAnchorPoint = Vector2.new(0.5, 0),
|
||||
ContainerPosition = UDim2.new(0.5, 0, 0, 36),
|
||||
AvatarHeadShotSize = 48,
|
||||
TopSizeY = 90,
|
||||
ClipsDescendants = false,
|
||||
PlayerViewportSize = UDim2.new(1, 0, 0.41, 0),
|
||||
AssetListSize = UDim2.new(1, 0, 0.59, -42),
|
||||
AssetListPosition = UDim2.new(0, 0, 0.41, 42),
|
||||
CloseButtonPosition = UDim2.new(0, 6, 0, -20),
|
||||
DetailsThumbnailFrameSize = UDim2.new(1, 0, 0.4, 0),
|
||||
DetailsThumbnailFramePosition = UDim2.new(0, 0, 0, 15),
|
||||
DetailsThumbnailAnchorPoint = Vector2.new(0.5, 0.5),
|
||||
DetailsThumbnailPosition = UDim2.new(0.5, 0, 0.5, 0),
|
||||
DetailsThumbnailARDominantAxis = Enum.DominantAxis.Height,
|
||||
DetailsFramePosition = UDim2.new(0, 15, 0.4, 15),
|
||||
DetailsFrameSize = UDim2.new(1, -30, 0.6, -30),
|
||||
AssetCardMaxSizeX = 108,
|
||||
AssetCardMaxSizeY = 157,
|
||||
DefaultCameraOffset = CFrame.new(0, 2, -5),
|
||||
ToolOffset = CFrame.new(0, 2, -8),
|
||||
TryOnPosition = UDim2.new(0, 0, 0, 15),
|
||||
TryOnSize = UDim2.new(1, 0, 0.4, 0),
|
||||
BorderPaddingSize = 20,
|
||||
AssetTextMaxSize = 14,
|
||||
}
|
||||
|
||||
setmetatable(CompactView,
|
||||
{
|
||||
__newindex = function(t, key, index)
|
||||
end,
|
||||
__index = function(t, index)
|
||||
error("CompactView table has no value: " .. tostring(index))
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
return CompactView
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local SetDetailsInformation = require(InspectAndBuyFolder.Actions.SetDetailsInformation)
|
||||
local Url = require(CorePackages.AppTempCommon.LuaApp.Http.Url)
|
||||
local getSelectionImageObjectRegular = require(InspectAndBuyFolder.getSelectionImageObjectRegular)
|
||||
|
||||
local GetFFlagUseThumbnailUrl = require(game:GetService("CoreGui").RobloxGui.Modules.Common.Flags.GetFFlagUseThumbnailUrl)
|
||||
|
||||
local AssetCard = Roact.Component:extend("AssetCard")
|
||||
|
||||
function AssetCard:init()
|
||||
self.selectedImage = getSelectionImageObjectRegular()
|
||||
end
|
||||
|
||||
function AssetCard:render()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local equipped = self.props.equipped
|
||||
local assetInfo = self.props.assetInfo
|
||||
local openDetails = self.props.openDetails
|
||||
local assetCardSizeX = self.props.assetCardSizeX
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
BackgroundTransparency = 0,
|
||||
BorderSizePixel = 0,
|
||||
BackgroundColor3 = Colors.Gray2,
|
||||
Selectable = true,
|
||||
SelectionImageObject = self.selectedImage,
|
||||
[Roact.Event.Activated] = function()
|
||||
openDetails(assetInfo.assetId)
|
||||
end,
|
||||
}, {
|
||||
AssetThumbnail = Roact.createElement("ImageButton", {
|
||||
Size = UDim2.new(1, 0, 0, assetCardSizeX),
|
||||
BackgroundTransparency = 0,
|
||||
BorderSizePixel = 0,
|
||||
BackgroundColor3 = Colors.Graphite,
|
||||
Selectable = false,
|
||||
AutoButtonColor = false,
|
||||
Image = GetFFlagUseThumbnailUrl() and "rbxthumb://type=Asset&id=" .. assetInfo.assetId .. "&w=150&h=150" or Url.BASE_URL_SECURE .."/asset-thumbnail/image?assetId=" ..assetInfo.assetId
|
||||
.."&width=150&height=150&format=png",
|
||||
[Roact.Event.Activated] = function()
|
||||
openDetails(assetInfo.assetId)
|
||||
end,
|
||||
}),
|
||||
AssetNameBackground = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, -assetCardSizeX),
|
||||
Position = UDim2.new(0, 0, 0, assetCardSizeX),
|
||||
BorderSizePixel = 0,
|
||||
BackgroundColor3 = Color3.fromRGB(44, 45, 47),
|
||||
}, {
|
||||
AssetName = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, -10, 1, -10),
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
Position = UDim2.new(0, 5, 0, 5),
|
||||
TextWrapped = true,
|
||||
Text = assetInfo.name or "",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
TextSize = 12,
|
||||
TextScaled = true,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextColor3 = Color3.new(1, 1, 1),
|
||||
}, {
|
||||
UITextSizeConstraint = Roact.createElement("UITextSizeConstraint", {
|
||||
MaxTextSize = viewMapping.AssetTextMaxSize,
|
||||
})
|
||||
}),
|
||||
}),
|
||||
EquippedFrame = Roact.createElement("ImageLabel", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = 2,
|
||||
Visible = equipped,
|
||||
Image = "rbxasset://textures/ui/InspectMenu/gr-item-selector.png",
|
||||
ImageColor3 = Colors.Green,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(2.5, 2.5, 2.5, 2.5),
|
||||
}),
|
||||
Corner = Roact.createElement("ImageLabel", {
|
||||
Position = UDim2.new(1, -25, 0, 0),
|
||||
Size = UDim2.new(0, 25, 0, 25),
|
||||
BackgroundTransparency = 1,
|
||||
ZIndex = 2,
|
||||
Visible = equipped,
|
||||
Image = "rbxasset://textures/ui/InspectMenu/gr-item-selector-triangle.png",
|
||||
ImageColor3 = Colors.Green,
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = props.assetInfo.assetId
|
||||
|
||||
return {
|
||||
view = state.view,
|
||||
equipped = state.equippedAssets[assetId] == true,
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
openDetails = function(assetId)
|
||||
dispatch(SetDetailsInformation(true, assetId))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(AssetCard)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local AssetCard = require(script.Parent.AssetCard)
|
||||
|
||||
local assetInfoProp = {
|
||||
assetId = "1",
|
||||
name = "mock",
|
||||
}
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(AssetCard, {
|
||||
assetInfo = assetInfoProp,
|
||||
assetCardSizeX = 10,
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local DetailsText = require(InspectAndBuyFolder.Components.DetailsText)
|
||||
local DetailsThumbnail = require(InspectAndBuyFolder.Components.DetailsThumbnail)
|
||||
local DetailsDescription = require(InspectAndBuyFolder.Components.DetailsDescription)
|
||||
local DetailsButtons = require(InspectAndBuyFolder.Components.DetailsButtons)
|
||||
local TryOnViewport = require(InspectAndBuyFolder.Components.TryOnViewport)
|
||||
local GetAssetBundles = require(InspectAndBuyFolder.Thunks.GetAssetBundles)
|
||||
local ReportOpenDetailsPage = require(InspectAndBuyFolder.Thunks.ReportOpenDetailsPage)
|
||||
local GetEconomyProductInfo = require(InspectAndBuyFolder.Thunks.GetEconomyProductInfo)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local UtilityFunctions = require(InspectAndBuyFolder.UtilityFunctions)
|
||||
|
||||
local AssetDetails = Roact.PureComponent:extend("AssetDetails")
|
||||
|
||||
function AssetDetails:setScrollingEnabled(enabled)
|
||||
local scrollingEnabled = self.state.scrollingEnabled
|
||||
|
||||
if scrollingEnabled ~= enabled then
|
||||
self:setState({
|
||||
scrollingEnabled = enabled
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function AssetDetails:init()
|
||||
self.state = {
|
||||
scrollingEnabled = true,
|
||||
}
|
||||
end
|
||||
|
||||
function AssetDetails:willUpdate(nextProps)
|
||||
if self.props.assetInfo ~= nextProps.assetInfo and nextProps.assetInfo then
|
||||
local getAssetBundles = self.props.getAssetBundles
|
||||
|
||||
if not nextProps.assetInfo.bundlesAssetIsIn then
|
||||
getAssetBundles(nextProps.assetInfo.assetId)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AssetDetails:didUpdate(prevProps)
|
||||
local reportOpenDetailsPage = self.props.reportOpenDetailsPage
|
||||
local assetInfo = self.props.assetInfo
|
||||
local prevAssetInfo = prevProps.assetInfo
|
||||
|
||||
if assetInfo ~= prevAssetInfo and assetInfo and assetInfo.bundlesAssetIsIn then
|
||||
local getEconomyProductInfo = self.props.getEconomyProductInfo
|
||||
local bundles = self.props.bundles
|
||||
local isBundle = assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn == 1
|
||||
local bundleId = isBundle and UtilityFunctions.getBundleId(assetInfo)
|
||||
local productId = isBundle and bundles[bundleId].productId or assetInfo.productId
|
||||
|
||||
if (not isBundle and assetInfo.owned == nil) or (isBundle and bundles[bundleId].owned == nil) then
|
||||
getEconomyProductInfo(productId, isBundle, bundleId)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
We need to report when the item details page was opened, but we need to know if it's showing a bundle
|
||||
or asset. So we have to check if we've gotten that data yet or not.
|
||||
]]
|
||||
local startedViewingDetails = self.props.detailsInformation.viewingDetails
|
||||
and not prevProps.detailsInformation.viewingDetails
|
||||
local obtainedBundlesInfo = prevAssetInfo and not prevAssetInfo.bundlesAssetIsIn
|
||||
and assetInfo and assetInfo.bundlesAssetIsIn
|
||||
|
||||
if ((assetInfo and assetInfo.bundlesAssetIsIn and startedViewingDetails) or obtainedBundlesInfo) then
|
||||
reportOpenDetailsPage(assetInfo)
|
||||
end
|
||||
end
|
||||
|
||||
function AssetDetails:render()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local detailsInformation = self.props.detailsInformation
|
||||
local assetInfo = self.props.assetInfo or {}
|
||||
local localPlayerModel = self.props.localPlayerModel
|
||||
local scrollingEnabled = self.state.scrollingEnabled
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Position = UDim2.new(0, viewMapping.BorderPaddingSize, 0, 0),
|
||||
Size = UDim2.new(1, -(2 * viewMapping.BorderPaddingSize), 1, 0),
|
||||
BackgroundTransparency = 0,
|
||||
BackgroundColor3 = Colors.Carbon,
|
||||
BorderSizePixel = 0,
|
||||
-- Do not show asset information until we know if a bundle should be shown instead.
|
||||
Visible = detailsInformation.viewingDetails and assetInfo.bundlesAssetIsIn ~= nil,
|
||||
}, {
|
||||
DetailsButtons = Roact.createElement(DetailsButtons),
|
||||
|
||||
Container = Roact.createElement("ScrollingFrame", {
|
||||
BackgroundTransparency = 1,
|
||||
ScrollBarThickness = 0,
|
||||
Size = UDim2.new(1, 0, 1, -55),
|
||||
ScrollingEnabled = scrollingEnabled,
|
||||
}, {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, 10),
|
||||
}),
|
||||
DetailsThumbnail = Roact.createElement(DetailsThumbnail),
|
||||
TryOnViewport = localPlayerModel and Roact.createElement(TryOnViewport, {
|
||||
localPlayerModel = localPlayerModel,
|
||||
setScrollingEnabled = function(enabled)
|
||||
self:setScrollingEnabled(enabled)
|
||||
end,
|
||||
}),
|
||||
DetailsText = Roact.createElement(DetailsText),
|
||||
DetailsDescription = Roact.createElement(DetailsDescription),
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
view = state.view,
|
||||
detailsInformation = state.detailsInformation,
|
||||
assetInfo = state.assets[assetId],
|
||||
bundles = state.bundles,
|
||||
}
|
||||
end,
|
||||
|
||||
function(dispatch)
|
||||
return {
|
||||
getAssetBundles = function(assetId)
|
||||
dispatch(GetAssetBundles(assetId))
|
||||
end,
|
||||
getEconomyProductInfo = function(productId, isBundle, bundleId)
|
||||
dispatch(GetEconomyProductInfo(productId, isBundle, bundleId))
|
||||
end,
|
||||
reportOpenDetailsPage = function(assetInfo)
|
||||
dispatch(ReportOpenDetailsPage(assetInfo))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(AssetDetails)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local AssetDetails = require(script.Parent.AssetDetails)
|
||||
|
||||
local mockModel = require(InspectAndBuyFolder.Test.getMockModel)()
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(AssetDetails, {
|
||||
localPlayerModel = mockModel,
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local AssetCard = require(InspectAndBuyFolder.Components.AssetCard)
|
||||
|
||||
local AssetList = Roact.PureComponent:extend("AssetList")
|
||||
|
||||
local CARD_PADDING = 10
|
||||
local FRAME_PADDING = 15
|
||||
local ASSET_CARD_RATIO = 0.68
|
||||
|
||||
function AssetList:calculateCanvasSize(numCardsPerRow, numAssetCards, cardSizeY)
|
||||
local rbx = self.gridFrameRef.current
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
|
||||
if rbx then
|
||||
local positionDifference = viewMapping.TopSizeY + FRAME_PADDING
|
||||
local canvasSize = math.ceil(numAssetCards / numCardsPerRow) * (cardSizeY + CARD_PADDING)
|
||||
+ positionDifference
|
||||
rbx.Parent.CanvasSize = UDim2.new(1, 0, 0, canvasSize)
|
||||
end
|
||||
end
|
||||
|
||||
function AssetList:init()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
self.gridFrameRef = Roact.createRef()
|
||||
|
||||
self.state = {
|
||||
assetCardSizeX = viewMapping.AssetCardMaxSizeX,
|
||||
assetCardSizeY = viewMapping.AssetCardMaxSizeY,
|
||||
}
|
||||
end
|
||||
|
||||
function AssetList:render()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local assets = self.props.assets
|
||||
local numCardsPerRow = viewMapping.MaxAssetCardsPerRow
|
||||
local assetCardSizeX = self.state.assetCardSizeX
|
||||
local assetCardSizeY = self.state.assetCardSizeY
|
||||
local assetCards = {}
|
||||
local numAssets = 0
|
||||
|
||||
assetCards["UIGridLayout"] = Roact.createElement("UIGridLayout", {
|
||||
CellPadding = UDim2.new(0, CARD_PADDING, 0, CARD_PADDING),
|
||||
CellSize = UDim2.new(0, assetCardSizeX, 0, assetCardSizeY),
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
FillDirectionMaxCells = numCardsPerRow,
|
||||
})
|
||||
|
||||
for _, assetInfo in pairs(assets) do
|
||||
numAssets = numAssets + 1
|
||||
assetCards[assetInfo.assetId] = Roact.createElement(AssetCard, {
|
||||
assetInfo = assetInfo,
|
||||
assetCardSizeX = assetCardSizeX,
|
||||
})
|
||||
end
|
||||
|
||||
self:calculateCanvasSize(numCardsPerRow, numAssets, assetCardSizeY)
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, -(2 * viewMapping.BorderPaddingSize), 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
LayoutOrder = 3,
|
||||
[Roact.Ref] = self.gridFrameRef,
|
||||
[Roact.Change.AbsoluteSize] = function(rbx)
|
||||
spawn(function()
|
||||
self:resize()
|
||||
end)
|
||||
end,
|
||||
}, assetCards)
|
||||
end
|
||||
|
||||
function AssetList:didMount()
|
||||
self:resize()
|
||||
self.mounted = true
|
||||
end
|
||||
|
||||
function AssetList:didUpdate(prevProps)
|
||||
if self.props.view ~= prevProps.view then
|
||||
self:resize()
|
||||
end
|
||||
|
||||
if self.mounted and self.props.gamepadEnabled and not self.props.detailsInformation.viewingDetails
|
||||
and self.props.visible then
|
||||
GuiService.SelectedCoreObject = self.gridFrameRef.current:FindFirstChildWhichIsA("GuiObject")
|
||||
end
|
||||
end
|
||||
|
||||
function AssetList:willUnmount()
|
||||
self.mounted = false
|
||||
end
|
||||
|
||||
function AssetList:resize()
|
||||
local rbx = self.gridFrameRef.current
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
|
||||
if rbx then
|
||||
local numCardsPerRow = viewMapping.MaxAssetCardsPerRow
|
||||
|
||||
local width = math.floor((rbx.AbsoluteSize.X - CARD_PADDING *
|
||||
(numCardsPerRow - 1)) / numCardsPerRow)
|
||||
local assetCardSizeX = math.min(width, viewMapping.AssetCardMaxSizeX)
|
||||
local assetCardSizeY = math.min(width / ASSET_CARD_RATIO, viewMapping.AssetCardMaxSizeY)
|
||||
|
||||
if self.state.assetCardSizeX ~= assetCardSizeX or self.state.assetCardSizeY ~= assetCardSizeY then
|
||||
self:setState({
|
||||
assetCardSizeX = assetCardSizeX,
|
||||
assetCardSizeY = assetCardSizeY,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
view = state.view,
|
||||
visible = state.visible,
|
||||
assets = state.assets,
|
||||
detailsInformation = state.detailsInformation,
|
||||
gamepadEnabled = state.gamepadEnabled,
|
||||
}
|
||||
end
|
||||
)(AssetList)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local AssetList = require(script.Parent.AssetList)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement("ScrollingFrame", {}, {
|
||||
Roact.createElement(AssetList, {})
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
|
||||
local AvatarHeadShot = Roact.PureComponent:extend("AvatarHeadShot")
|
||||
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local CoreGuiModules = RobloxGui:WaitForChild("Modules")
|
||||
|
||||
local GetFFlagUseThumbnailUrl = require(CoreGuiModules.Common.Flags.GetFFlagUseThumbnailUrl)
|
||||
|
||||
local LegacyThumbnailUrls = require(CoreGuiModules.Common.LegacyThumbnailUrls)
|
||||
|
||||
local HEADSHOT_THUMBNAIL_SIZE = 48
|
||||
local HEAD_SHOT_URL = LegacyThumbnailUrls.Headshot
|
||||
|
||||
function AvatarHeadShot:render()
|
||||
local playerId = self.props.playerId
|
||||
|
||||
local headshotUrl = nil
|
||||
if playerId and playerId ~= "" then
|
||||
headshotUrl = HEAD_SHOT_URL:format(HEADSHOT_THUMBNAIL_SIZE, HEADSHOT_THUMBNAIL_SIZE, playerId)
|
||||
if GetFFlagUseThumbnailUrl() then
|
||||
headshotUrl = HEAD_SHOT_URL:format(playerId, HEADSHOT_THUMBNAIL_SIZE, HEADSHOT_THUMBNAIL_SIZE)
|
||||
end
|
||||
end
|
||||
|
||||
return Roact.createElement("ImageLabel", {
|
||||
Size = UDim2.new(0, 48, 0, 48),
|
||||
BackgroundColor3 = Colors.GrayBackground,
|
||||
BorderSizePixel = 0,
|
||||
Image = headshotUrl,
|
||||
LayoutOrder = 1,
|
||||
}, {
|
||||
MaskFrame = Roact.createElement("ImageLabel", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
Image = "rbxasset://textures/ui/LuaApp/graphic/gr-avatar mask-90x90.png",
|
||||
ImageColor3 = Colors.Carbon,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
playerId = state.playerId,
|
||||
}
|
||||
end
|
||||
)(AvatarHeadShot)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local AvatarHeadShot = require(script.Parent.AvatarHeadShot)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(AvatarHeadShot, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local UserInputService = game:GetService('UserInputService')
|
||||
local RunService = game:GetService('RunService')
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local rebuildJoints = require(InspectAndBuyFolder.rebuildJoints)
|
||||
|
||||
local CHARACTER_ROTATION_SPEED = .0065
|
||||
local STICK_ROTATION_MULTIPLIER = 3
|
||||
local THUMBSTICK_DEADZONE = 0.2
|
||||
local AUTO_ROTATE_WAIT_TIME = 2
|
||||
local AUTO_SPIN_STEP = math.rad(0.70)
|
||||
|
||||
local AvatarViewport = Roact.PureComponent:extend("AvatarViewport")
|
||||
|
||||
function AvatarViewport:init()
|
||||
self.viewportFrameRef = Roact.createRef()
|
||||
self.connections = {}
|
||||
self.xRotation = 0
|
||||
self.yRotation = 0
|
||||
self.rotationDeltaX = 0
|
||||
self.rotationDeltaY = 0
|
||||
self.mouseOrTouchDown = false
|
||||
self.delta = 0
|
||||
self.keys = {}
|
||||
self.lastPosition = Vector3.new(0, 0, 0)
|
||||
self.viewportCamera = Instance.new("Camera")
|
||||
self.viewportCamera.CameraType = Enum.CameraType.Scriptable
|
||||
self.model = self.props.model
|
||||
self.lastInputTime = AUTO_ROTATE_WAIT_TIME
|
||||
end
|
||||
|
||||
function AvatarViewport:didMount()
|
||||
if self.props.humanoidDescription and self.model then
|
||||
self.viewportCamera.Parent = self.viewportFrameRef.current
|
||||
self.viewportFrameRef.current.CurrentCamera = self.viewportCamera
|
||||
self:setRotation()
|
||||
rebuildJoints(self.model)
|
||||
self.model.Parent = self.viewportFrameRef.current
|
||||
end
|
||||
|
||||
self.isMounted = true
|
||||
self:handleSpin()
|
||||
self:setUpGamepad()
|
||||
end
|
||||
|
||||
function AvatarViewport:didUpdate(prevProps, prevState)
|
||||
if self.props.visible and self.props.humanoidDescription ~= prevProps.humanoidDescription and self.model then
|
||||
coroutine.wrap(function()
|
||||
self.model.Parent = self.viewportFrameRef.current
|
||||
self.model.Humanoid:ApplyDescriptionClientServer(self.props.humanoidDescription)
|
||||
rebuildJoints(self.model)
|
||||
end)()
|
||||
end
|
||||
|
||||
self.gamepadRotating = false
|
||||
self.viewportCamera.Parent = self.viewportFrameRef.current
|
||||
self.viewportFrameRef.current.CurrentCamera = self.viewportCamera
|
||||
|
||||
if self.props.gamepadEnabled ~= prevProps.gamepadEnabled then
|
||||
if self.props.gamepadEnabled then
|
||||
self:setUpGamepad()
|
||||
else
|
||||
self:unbindActions()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Note: Because the camera is the object moving, the character will always be upright.
|
||||
function AvatarViewport:setRotation()
|
||||
local currentCharacter = self.model
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
|
||||
if currentCharacter then
|
||||
local hrp = currentCharacter.HumanoidRootPart
|
||||
local offset = viewMapping.DefaultCameraOffset
|
||||
if self.model:FindFirstChildOfClass("Tool") then
|
||||
offset = viewMapping.ToolOffset
|
||||
end
|
||||
|
||||
local cameraPosition = (
|
||||
CFrame.new(hrp.Position)
|
||||
* CFrame.Angles(0, -self.yRotation, 0)
|
||||
* CFrame.Angles(self.xRotation, 0, 0)
|
||||
* offset
|
||||
).p
|
||||
|
||||
self.viewportCamera.CFrame = CFrame.new(cameraPosition, hrp.Position)
|
||||
end
|
||||
end
|
||||
|
||||
function AvatarViewport:render()
|
||||
local size = self.props.size
|
||||
local position = self.props.position
|
||||
local visible = self.props.visible
|
||||
local backgroundTransparency = self.props.backgroundTransparency
|
||||
local setScrollingEnabled = self.props.setScrollingEnabled
|
||||
|
||||
return Roact.createElement("ViewportFrame", {
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Position = position,
|
||||
Size = size,
|
||||
BackgroundTransparency = backgroundTransparency,
|
||||
BackgroundColor3 = Colors.Graphite,
|
||||
BorderSizePixel = 0,
|
||||
Active = true,
|
||||
Visible = visible,
|
||||
[Roact.Ref] = self.viewportFrameRef,
|
||||
[Roact.Event.InputBegan] = function(rbx, input)
|
||||
if input.UserInputState == Enum.UserInputState.Begin then
|
||||
self.mouseOrTouchDown = true
|
||||
self.lastPosition = input.Position
|
||||
setScrollingEnabled(false)
|
||||
local changedConnection = UserInputService.InputChanged:connect(function(inputObject, gameProcessedEvent)
|
||||
if (inputObject.UserInputType == Enum.UserInputType.MouseMovement and self.mouseOrTouchDown)
|
||||
or inputObject.UserInputType == Enum.UserInputType.Touch then
|
||||
local touchDelta = (inputObject.Position - self.lastPosition)
|
||||
self.lastPosition = inputObject.Position
|
||||
self.yRotation = self.yRotation + touchDelta.x * CHARACTER_ROTATION_SPEED
|
||||
self.xRotation = self.xRotation + touchDelta.y * CHARACTER_ROTATION_SPEED
|
||||
self.lastInputTime = tick()
|
||||
end
|
||||
end)
|
||||
local endConnection = UserInputService.InputEnded:connect(function(inputObject, gameProcessedEvent)
|
||||
if inputObject.UserInputType == Enum.UserInputType.MouseButton1
|
||||
or inputObject.UserInputType == Enum.UserInputType.Touch then
|
||||
if self.mouseOrTouchDown then
|
||||
self.mouseOrTouchDown = false
|
||||
self.lastInputTime = tick()
|
||||
self:removeConnections()
|
||||
end
|
||||
setScrollingEnabled(true)
|
||||
end
|
||||
end)
|
||||
table.insert(self.connections, changedConnection)
|
||||
table.insert(self.connections, endConnection)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
--[[
|
||||
Handle rotating the avatar.
|
||||
]]
|
||||
function AvatarViewport:handleSpin()
|
||||
local isSpinning = false
|
||||
spawn(function()
|
||||
while self.isMounted do
|
||||
-- Automatically rotate the avatar if the user is not interacting with it.
|
||||
if tick() - self.lastInputTime > AUTO_ROTATE_WAIT_TIME and not self.mouseOrTouchDown
|
||||
and not self.gamepadRotating then
|
||||
isSpinning = true
|
||||
else
|
||||
isSpinning = false
|
||||
end
|
||||
|
||||
local function returnToDefaultPosition(current, maxStep)
|
||||
current = current % math.rad(360)
|
||||
if current > math.rad(180) then
|
||||
current = current + maxStep
|
||||
if current > math.rad(360) then
|
||||
return 0
|
||||
end
|
||||
else
|
||||
current = current - maxStep
|
||||
if current < 0 then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
return current
|
||||
end
|
||||
|
||||
if isSpinning then
|
||||
self.xRotation = returnToDefaultPosition(self.xRotation, math.rad(2))
|
||||
self.yRotation = self.yRotation + AUTO_SPIN_STEP
|
||||
elseif self.gamepadRotating then
|
||||
self.yRotation = self.yRotation + self.delta * self.rotationDeltaY
|
||||
self.xRotation = self.xRotation + self.delta * self.rotationDeltaX
|
||||
end
|
||||
|
||||
self:setRotation()
|
||||
self.delta = RunService.RenderStepped:wait()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function AvatarViewport:setUpGamepad()
|
||||
if UserInputService.GamepadEnabled then
|
||||
local gamepadInput = Vector2.new(0, 0)
|
||||
|
||||
ContextActionService:UnbindCoreAction("RotateModel")
|
||||
ContextActionService:BindCoreAction("RotateModel", function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Change then
|
||||
gamepadInput = inputObject.Position or gamepadInput
|
||||
gamepadInput = Vector2.new(gamepadInput.X, gamepadInput.Y)
|
||||
if math.abs(gamepadInput.X) > THUMBSTICK_DEADZONE or math.abs(gamepadInput.Y) > THUMBSTICK_DEADZONE then
|
||||
self.lastInputTime = tick()
|
||||
self.gamepadRotating = true
|
||||
self.rotationDeltaY = STICK_ROTATION_MULTIPLIER * gamepadInput.X
|
||||
self.rotationDeltaX = STICK_ROTATION_MULTIPLIER * gamepadInput.Y
|
||||
else
|
||||
self.gamepadRotating = false
|
||||
end
|
||||
end
|
||||
|
||||
return Enum.ContextActionResult.Sink
|
||||
end,
|
||||
false, Enum.KeyCode.Thumbstick2, Enum.KeyCode.ButtonA)
|
||||
end
|
||||
end
|
||||
|
||||
function AvatarViewport:unbindActions()
|
||||
ContextActionService:UnbindCoreAction("RotateModel")
|
||||
end
|
||||
|
||||
function AvatarViewport:removeConnections()
|
||||
for _, listener in pairs(self.connections) do
|
||||
listener:disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
function AvatarViewport:willUnmount()
|
||||
self.isMounted = false
|
||||
self:unbindActions()
|
||||
self:removeConnections()
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
view = state.view,
|
||||
tryingOnInfo = state.tryingOnInfo,
|
||||
gamepadEnabled = state.gamepadEnabled,
|
||||
}
|
||||
end
|
||||
)(AvatarViewport)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local AvatarViewport = require(script.Parent.AvatarViewport)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(AvatarViewport, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local TextService = game:GetService("TextService")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local PromptPurchase = require(InspectAndBuyFolder.Thunks.PromptPurchase)
|
||||
local getSelectionImageObjectRounded = require(InspectAndBuyFolder.getSelectionImageObjectRounded)
|
||||
|
||||
local TEXT_SIZE = 16
|
||||
local MIN_SIZE = 32
|
||||
local ROBUX_ICON_SIZE = 16
|
||||
local BUTTON_PADDING = 10
|
||||
local ROBLOX_CREATOR_ID = "1"
|
||||
|
||||
local BuyButton = Roact.PureComponent:extend("BuyButton")
|
||||
|
||||
function BuyButton:init()
|
||||
self.selectedImage = getSelectionImageObjectRounded()
|
||||
end
|
||||
|
||||
function BuyButton:render()
|
||||
local itemType = self.props.itemType
|
||||
local itemId = self.props.itemId
|
||||
local showRobuxIcon = self.props.showRobuxIcon
|
||||
local forSale = self.props.forSale
|
||||
local promptPurchase = self.props.promptPurchase
|
||||
local buyText = self.props.buyText
|
||||
local buyButtonRef = self.props.buyButtonRef
|
||||
local size = UDim2.new(0, self:getBuyButtonTextSize(buyText), 1, 0)
|
||||
local assetInfo = self.props.assetInfo
|
||||
local creatorId = assetInfo and assetInfo.creatorId or 0
|
||||
local sizeXAdjustment = creatorId == ROBLOX_CREATOR_ID and -32 or -BUTTON_PADDING / 2
|
||||
local transparencyOverride = 0
|
||||
|
||||
if not forSale then
|
||||
transparencyOverride = 0.5
|
||||
end
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0.5, sizeXAdjustment, 0, 44),
|
||||
Image = "rbxasset://textures/ui/InspectMenu/Button_white.png",
|
||||
ImageColor3 = Colors.Green,
|
||||
SelectionImageObject = self.selectedImage,
|
||||
LayoutOrder = 3,
|
||||
ImageTransparency = transparencyOverride,
|
||||
[Roact.Ref] = buyButtonRef,
|
||||
[Roact.Event.Activated] = function()
|
||||
if forSale then
|
||||
promptPurchase(itemId, itemType)
|
||||
end
|
||||
end,
|
||||
}, {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
}),
|
||||
RobuxIcon = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, ROBUX_ICON_SIZE, 0, ROBUX_ICON_SIZE),
|
||||
Image = "rbxasset://textures/ui/common/robux_small.png",
|
||||
ImageTransparency = transparencyOverride,
|
||||
ImageColor3 = Colors.White,
|
||||
LayoutOrder = 1,
|
||||
Visible = showRobuxIcon,
|
||||
}),
|
||||
BuyText = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = size,
|
||||
Text = buyText,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = TEXT_SIZE,
|
||||
TextColor3 = Colors.White,
|
||||
TextTransparency = transparencyOverride,
|
||||
LayoutOrder = 2,
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
function BuyButton:getBuyButtonTextSize(buyText)
|
||||
if self.props.buyButtonRef.current then
|
||||
local buyButtonTextSize = TextService:GetTextSize(buyText,
|
||||
TEXT_SIZE, Enum.Font.Gotham, Vector2.new(self.props.buyButtonRef.current.AbsoluteSize.X, 5000))
|
||||
self.props.buyButtonRef.Size = UDim2.new(0, 1, 0, buyButtonTextSize.Y)
|
||||
return buyButtonTextSize.X > MIN_SIZE and buyButtonTextSize.X or MIN_SIZE
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
locale = state.locale,
|
||||
view = state.view,
|
||||
assetInfo = state.assets[assetId],
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
promptPurchase = function(itemId, itemType)
|
||||
dispatch(PromptPurchase(itemId, itemType))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(BuyButton)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local BuyButton = require(script.Parent.BuyButton)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(BuyButton, {
|
||||
buyButtonRef = Roact.createRef(),
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetDetailsInformation = require(InspectAndBuyFolder.Actions.SetDetailsInformation)
|
||||
local SetTryingOnInfo = require(InspectAndBuyFolder.Actions.SetTryingOnInfo)
|
||||
|
||||
local CloseButton = Roact.PureComponent:extend("CloseButton")
|
||||
|
||||
function CloseButton:render()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local viewingDetails = self.props.detailsInformation.viewingDetails
|
||||
local closeDetails = self.props.closeDetails
|
||||
local setTryingOnInfo = self.props.setTryingOnInfo
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
Size = UDim2.new(0, 26, 0, 26),
|
||||
Position = viewMapping.CloseButtonPosition,
|
||||
BackgroundTransparency = 1,
|
||||
Selectable = false,
|
||||
Image = viewingDetails and "rbxasset://textures/ui/InspectMenu/caret_tail_left.png"
|
||||
or "rbxasset://textures/ui/InspectMenu/x.png",
|
||||
ZIndex = 2,
|
||||
[Roact.Event.Activated] = function()
|
||||
if viewingDetails then
|
||||
closeDetails()
|
||||
setTryingOnInfo(false, nil)
|
||||
else
|
||||
GuiService:CloseInspectMenu()
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
view = state.view,
|
||||
detailsInformation = state.detailsInformation,
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
closeDetails = function()
|
||||
dispatch(SetDetailsInformation(false, nil))
|
||||
end,
|
||||
setTryingOnInfo = function(tryingOn, assetId)
|
||||
dispatch(SetTryingOnInfo(tryingOn, assetId))
|
||||
end
|
||||
}
|
||||
end
|
||||
)(CloseButton)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local CloseButton = require(script.Parent.CloseButton)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(CloseButton, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local InitialView = require(InspectAndBuyFolder.Components.InitialView)
|
||||
local AssetDetails = require(InspectAndBuyFolder.Components.AssetDetails)
|
||||
local CloseButton = require(InspectAndBuyFolder.Components.CloseButton)
|
||||
local NoInventoryNotice = require(InspectAndBuyFolder.Components.NoInventoryNotice)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local Constants = require(InspectAndBuyFolder.Constants)
|
||||
|
||||
local FFlagInspectAndBuyResetSelectedCoreObject = game:DefineFastFlag("InspectAndBuyResetSelectedCoreObject", false)
|
||||
|
||||
local Container = Roact.PureComponent:extend("Container")
|
||||
|
||||
function Container:init()
|
||||
self.frameRef = Roact.createRef()
|
||||
self.lastSavedSelectedCoreGui = nil
|
||||
end
|
||||
|
||||
function Container:didMount()
|
||||
local gamepadEnabled = self.props.gamepadEnabled
|
||||
|
||||
self.selectedCoreObjectChangedSignal = GuiService:GetPropertyChangedSignal('SelectedCoreObject'):connect(function()
|
||||
if self.props.visible and GuiService.SelectedCoreObject then
|
||||
self.lastSavedSelectedCoreGui = GuiService.SelectedCoreObject
|
||||
end
|
||||
end)
|
||||
|
||||
if gamepadEnabled then
|
||||
GuiService:AddSelectionParent("InspectMenu", self.frameRef.current)
|
||||
end
|
||||
end
|
||||
|
||||
function Container:didUpdate(prevProps)
|
||||
local visible = self.props.visible
|
||||
local gamepadEnabled = self.props.gamepadEnabled
|
||||
|
||||
if visible and gamepadEnabled and self.frameRef.current then
|
||||
GuiService:RemoveSelectionGroup("InspectMenu")
|
||||
GuiService:AddSelectionParent("InspectMenu", self.frameRef.current)
|
||||
if self.lastSavedSelectedCoreGui then
|
||||
GuiService.SelectedCoreObject = self.lastSavedSelectedCoreGui
|
||||
end
|
||||
elseif (not gamepadEnabled and prevProps.gamepadEnabled) or not visible then
|
||||
GuiService:RemoveSelectionGroup("InspectMenu")
|
||||
end
|
||||
|
||||
if visible and not prevProps.visible then
|
||||
GuiService.SelectedCoreObject = self.lastSavedSelectedCoreGui
|
||||
end
|
||||
end
|
||||
|
||||
function Container:willUnmount()
|
||||
self.selectedCoreObjectChangedSignal:disconnect()
|
||||
GuiService:RemoveSelectionGroup("InspectMenu")
|
||||
|
||||
if FFlagInspectAndBuyResetSelectedCoreObject then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Container:render()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local localPlayerModel = self.props.localPlayerModel
|
||||
local visible = self.props.visible
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
Size = UDim2.new(1, 0, 1, 46),
|
||||
Position = UDim2.new(0, 0, 0, -46),
|
||||
BackgroundColor3 = viewMapping.ContainerBackgroundColor,
|
||||
BackgroundTransparency = viewMapping.ContainerBackgroundTransparency,
|
||||
Visible = visible,
|
||||
Selectable = false,
|
||||
AutoButtonColor = false,
|
||||
AutoLocalize = false,
|
||||
[Roact.Event.Activated] = function()
|
||||
if view == Constants.View.Wide then
|
||||
GuiService:CloseInspectMenu()
|
||||
end
|
||||
end,
|
||||
}, {
|
||||
MainContainer = Roact.createElement("ImageButton", {
|
||||
AnchorPoint = viewMapping.ContainerAnchorPoint,
|
||||
Size = viewMapping.ContainerSize,
|
||||
Position = viewMapping.ContainerPosition,
|
||||
BackgroundColor3 = Colors.Carbon,
|
||||
BorderSizePixel = 0,
|
||||
Active = true,
|
||||
AutoButtonColor = false,
|
||||
Selectable = false,
|
||||
[Roact.Ref] = self.frameRef,
|
||||
}, {
|
||||
AspectRatioConstraint = viewMapping.UseContainerAspectRatio and Roact.createElement("UIAspectRatioConstraint", {
|
||||
AspectRatio = viewMapping.ContainerAspectRatio,
|
||||
}),
|
||||
CloseButton = Roact.createElement(CloseButton),
|
||||
InitialView = Roact.createElement(InitialView),
|
||||
AssetDetails = Roact.createElement(AssetDetails, {
|
||||
localPlayerModel = localPlayerModel,
|
||||
}),
|
||||
NoInventoryNotice = Roact.createElement(NoInventoryNotice),
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
view = state.view,
|
||||
visible = state.visible,
|
||||
gamepadEnabled = state.gamepadEnabled,
|
||||
}
|
||||
end
|
||||
)(Container)
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local Container = require(script.Parent.Container)
|
||||
|
||||
local mockModel = require(InspectAndBuyFolder.Test.getMockModel)()
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(Container, {
|
||||
humanoidDescription = Instance.new("HumanoidDescription"),
|
||||
inspectingModel = mockModel,
|
||||
localPlayerModel = mockModel,
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local Players = game:GetService("Players")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local RobloxTranslator = require(CoreGui.RobloxGui.Modules.RobloxTranslator)
|
||||
local Constants = require(InspectAndBuyFolder.Constants)
|
||||
local UtilityFunctions = require(InspectAndBuyFolder.UtilityFunctions)
|
||||
local FavoritesButton = require(InspectAndBuyFolder.Components.FavoritesButton)
|
||||
local TryOnButton = require(InspectAndBuyFolder.Components.TryOnButton)
|
||||
local BuyButton = require(InspectAndBuyFolder.Components.BuyButton)
|
||||
|
||||
local GetFFlagLuaPremiumCatalogIGIAB
|
||||
= require(CoreGui.RobloxGui.Modules.Flags.GetFFlagLuaPremiumCatalogIGIAB)
|
||||
|
||||
local DetailsButtons = Roact.PureComponent:extend("DetailsButtons")
|
||||
|
||||
local OFFSALE_KEY = "InGame.InspectMenu.Label.Offsale"
|
||||
local LIMITED_KEY = "InGame.InspectMenu.Label.Limited"
|
||||
local OWNED_KEY = "InGame.InspectMenu.Label.Owned"
|
||||
local PREMIUM_ONLY_KEY = "InGame.InspectMenu.Label.PremiumOnly"
|
||||
local ROBLOX_CREATOR_ID = "1"
|
||||
|
||||
--[[
|
||||
If an asset is an animation (or emotes in the future) we cannot support
|
||||
try on functionality.
|
||||
]]
|
||||
local function isAnimationAsset(assetTypeId)
|
||||
return Constants.AnimationAssetTypes[assetTypeId] ~= nil
|
||||
end
|
||||
|
||||
local function getBuyText(itemInfo, locale)
|
||||
local buyText
|
||||
|
||||
if itemInfo.owned then
|
||||
buyText = RobloxTranslator:FormatByKeyForLocale(OWNED_KEY, locale)
|
||||
elseif itemInfo.isLimited then
|
||||
buyText = RobloxTranslator:FormatByKeyForLocale(LIMITED_KEY, locale)
|
||||
elseif not itemInfo.isForSale and not itemInfo.isLimited then
|
||||
buyText = RobloxTranslator:FormatByKeyForLocale(OFFSALE_KEY, locale)
|
||||
elseif itemInfo.isForSale then
|
||||
if GetFFlagLuaPremiumCatalogIGIAB() then
|
||||
if itemInfo.premiumPricing ~= nil then
|
||||
if itemInfo.price == nil and Players.LocalPlayer.MembershipType ~= Enum.MembershipType.Premium then
|
||||
buyText = RobloxTranslator:FormatByKeyForLocale(PREMIUM_ONLY_KEY, locale)
|
||||
else
|
||||
buyText = itemInfo.premiumPricing.premiumPriceInRobux
|
||||
end
|
||||
else
|
||||
buyText = itemInfo.price
|
||||
end
|
||||
else
|
||||
buyText = itemInfo.price
|
||||
end
|
||||
end
|
||||
return buyText
|
||||
end
|
||||
|
||||
function DetailsButtons:init()
|
||||
self.tryOnButtonRef = Roact.createRef()
|
||||
self.buyButtonRef = Roact.createRef()
|
||||
self.favoriteButtonRef = Roact.createRef()
|
||||
end
|
||||
|
||||
function DetailsButtons:didUpdate(prevProps)
|
||||
local detailsInformation = self.props.detailsInformation
|
||||
local gamepadEnabled = self.props.gamepadEnabled
|
||||
|
||||
if (prevProps.assetInfo.bundlesAssetIsIn == nil and self.props.assetInfo.bundlesAssetIsIn ~= nil)
|
||||
and detailsInformation.viewingDetails then
|
||||
local assetInfo = self.props.assetInfo
|
||||
local showTryOn = not isAnimationAsset(assetInfo.assetTypeId)
|
||||
local visible = self.props.visible
|
||||
|
||||
if gamepadEnabled and visible then
|
||||
local creatorId = assetInfo and assetInfo.creatorId or 0
|
||||
|
||||
if creatorId == ROBLOX_CREATOR_ID then
|
||||
GuiService.SelectedCoreObject = self.favoriteButtonRef.current
|
||||
elseif showTryOn then
|
||||
GuiService.SelectedCoreObject = self.tryOnButtonRef.current
|
||||
else
|
||||
GuiService.SelectedCoreObject = self.buyButtonRef.current
|
||||
end
|
||||
end
|
||||
elseif self.props.assetInfo.bundlesAssetIsIn == nil and detailsInformation.viewingDetails and gamepadEnabled then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
function DetailsButtons:render()
|
||||
local locale = self.props.locale
|
||||
local assetInfo = self.props.assetInfo
|
||||
local bundleInfo = self.props.bundleInfo
|
||||
local isLimited = assetInfo.isLimited or false
|
||||
local showRobuxIcon = false
|
||||
local showTryOn = false
|
||||
local buyText, forSale, partOfBundle, bundleId, itemType, itemId
|
||||
if assetInfo then
|
||||
partOfBundle = assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn == 1
|
||||
if partOfBundle then
|
||||
bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
itemType = Constants.ItemType.Bundle
|
||||
itemId = bundleId
|
||||
if bundleInfo[bundleId] then
|
||||
buyText = getBuyText(bundleInfo[bundleId], locale)
|
||||
forSale = bundleInfo[bundleId].isForSale and not bundleInfo[bundleId].owned
|
||||
showRobuxIcon = bundleInfo[bundleId].price ~= nil and not bundleInfo[bundleId].owned and forSale
|
||||
end
|
||||
else
|
||||
itemType = Constants.ItemType.Asset
|
||||
itemId = assetInfo.assetId
|
||||
forSale = assetInfo.isForSale and not assetInfo.owned and not isLimited and assetInfo.owned ~= nil
|
||||
if GetFFlagLuaPremiumCatalogIGIAB() and forSale and assetInfo.price == nil and assetInfo.premiumPricing ~= nil then
|
||||
forSale = Players.LocalPlayer.MembershipType == Enum.MembershipType.Premium
|
||||
end
|
||||
buyText = getBuyText(assetInfo, locale)
|
||||
showRobuxIcon = assetInfo.price ~= nil and not assetInfo.owned and forSale
|
||||
end
|
||||
|
||||
showTryOn = not isAnimationAsset(assetInfo.assetTypeId)
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, 51),
|
||||
Position = UDim2.new(0, 0, 1, -51),
|
||||
}, {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
Padding = UDim.new(0, 10),
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
FavoriteButton = Roact.createElement(FavoritesButton, {
|
||||
favoriteButtonRef = self.favoriteButtonRef,
|
||||
}),
|
||||
TryOnButton = Roact.createElement(TryOnButton, {
|
||||
showTryOn = showTryOn,
|
||||
assetInfo = assetInfo,
|
||||
partOfBundle = partOfBundle,
|
||||
bundleId = bundleId,
|
||||
tryOnButtonRef = self.tryOnButtonRef,
|
||||
}),
|
||||
TryOnFiller = Roact.createElement("ImageLabel", {
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0.5, -32, 0, 44),
|
||||
Visible = not showTryOn,
|
||||
LayoutOrder = 2,
|
||||
Image = "",
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(5, 5, 120, 20),
|
||||
}),
|
||||
BuyButton = Roact.createElement(BuyButton, {
|
||||
itemType = itemType,
|
||||
itemId = itemId,
|
||||
showRobuxIcon = showRobuxIcon,
|
||||
forSale = forSale,
|
||||
buyText = buyText,
|
||||
buyButtonRef = self.buyButtonRef,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
visible = state.visible,
|
||||
assetInfo = state.assets[assetId] or {},
|
||||
detailsInformation = state.detailsInformation,
|
||||
bundleInfo = state.bundles,
|
||||
locale = state.locale,
|
||||
gamepadEnabled = state.gamepadEnabled,
|
||||
}
|
||||
end
|
||||
)(DetailsButtons)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local DetailsButtons = require(script.Parent.DetailsButtons)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(DetailsButtons, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local TextService = game:GetService("TextService")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local UtilityFunctions = require(InspectAndBuyFolder.UtilityFunctions)
|
||||
|
||||
local TEXT_SIZE_SMALL = 16
|
||||
local DETAILS_SIZES = 451
|
||||
|
||||
local DetailsDescription = Roact.PureComponent:extend("DetailsDescription")
|
||||
|
||||
--[[
|
||||
Sets the variables used to display text. If a bundle is
|
||||
being shown, they will display the bundle's data instead.
|
||||
]]
|
||||
function DetailsDescription:setText()
|
||||
local assetInfo = self.props.assetInfo or {}
|
||||
local partOfBundle = assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn == 1
|
||||
local bundleInfo = self.props.bundleInfo or {}
|
||||
|
||||
if partOfBundle then
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
if bundleInfo[bundleId] then
|
||||
self.description = bundleInfo[bundleId].description or ""
|
||||
end
|
||||
elseif assetInfo then
|
||||
self.description = assetInfo.description or ""
|
||||
end
|
||||
end
|
||||
|
||||
function DetailsDescription:init()
|
||||
self.description = ""
|
||||
self.descriptionRef = Roact.createRef()
|
||||
end
|
||||
|
||||
function DetailsDescription:render()
|
||||
self:setText()
|
||||
local size = self:calculateSize()
|
||||
|
||||
return Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 4,
|
||||
Size = UDim2.new(1, -20, 0, size),
|
||||
Text = self.description or "",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
TextSize = TEXT_SIZE_SMALL,
|
||||
TextWrapped = true,
|
||||
Font = Enum.Font.Gotham,
|
||||
TextColor3 = Colors.White,
|
||||
[Roact.Ref] = self.descriptionRef,
|
||||
})
|
||||
end
|
||||
|
||||
function DetailsDescription:calculateSize()
|
||||
if self.descriptionRef.current and self.props.assetInfo then
|
||||
local size = TextService:GetTextSize(self.description,
|
||||
TEXT_SIZE_SMALL, Enum.Font.Gotham, Vector2.new(self.descriptionRef.current.AbsoluteSize.X, 5000)).Y
|
||||
self.descriptionRef.current.Parent.CanvasSize = UDim2.new(1, 0, 0, DETAILS_SIZES + size)
|
||||
return size
|
||||
end
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
view = state.view,
|
||||
assetInfo = state.assets[assetId],
|
||||
detailsInformation = state.detailsInformation,
|
||||
bundleInfo = state.bundles,
|
||||
}
|
||||
end
|
||||
)(DetailsDescription)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local DetailsDescription = require(script.Parent.DetailsDescription)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(DetailsDescription, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Cryo = require(CorePackages.Cryo)
|
||||
local UIBlox = require(CorePackages.UIBlox)
|
||||
local UIBloxImages = UIBlox.App.ImageSet.Images
|
||||
local UIBloxIconSize = UIBlox.App.Constant.IconSize
|
||||
local Notification = require(InspectAndBuyFolder.Components.Notification)
|
||||
local Favorites = require(InspectAndBuyFolder.Components.Favorites)
|
||||
local UtilityFunctions = require(InspectAndBuyFolder.UtilityFunctions)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local Constants = require(InspectAndBuyFolder.Constants)
|
||||
local RobloxTranslator = require(CoreGui.RobloxGui.Modules.RobloxTranslator)
|
||||
local getSelectionImageObjectRegular = require(InspectAndBuyFolder.getSelectionImageObjectRegular)
|
||||
|
||||
local GetFFlagLuaPremiumCatalogIGIAB
|
||||
= require(CoreGui.RobloxGui.Modules.Flags.GetFFlagLuaPremiumCatalogIGIAB)
|
||||
|
||||
local PremiumIcon = UIBloxImages["icons/status/premium"]
|
||||
local BY_KEY = "InGame.InspectMenu.Label.By"
|
||||
local TEXT_SIZE_SMALL = 12
|
||||
local TITLE_TEXT_SIZE = 20
|
||||
local FAVORITES_SIZE = 16
|
||||
local ASSET_NAME_SIZE = 50
|
||||
local CREATOR_SIZE = 20
|
||||
local NOTIFICATION_SIZE = 45
|
||||
local PREMIUM_ICON_PADDING = 5
|
||||
|
||||
local DetailsText = Roact.PureComponent:extend("DetailsText")
|
||||
|
||||
function DetailsText:init()
|
||||
self.name = ""
|
||||
self.creator = ""
|
||||
self.selectedImage = getSelectionImageObjectRegular()
|
||||
end
|
||||
|
||||
--[[
|
||||
Sets the variables used to display text. If a bundle is
|
||||
being shown, they will display the bundle's data instead.
|
||||
]]
|
||||
function DetailsText:setText()
|
||||
local assetInfo = self.props.assetInfo or {}
|
||||
local partOfBundle = assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn == 1
|
||||
local bundleInfo = self.props.bundleInfo or {}
|
||||
|
||||
if partOfBundle then
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
if bundleInfo[bundleId] then
|
||||
self.name = bundleInfo[bundleId].name or ""
|
||||
self.creator = bundleInfo[bundleId].creatorName or ""
|
||||
end
|
||||
elseif assetInfo then
|
||||
self.name = assetInfo.name or ""
|
||||
self.creator = assetInfo.creatorName or ""
|
||||
end
|
||||
end
|
||||
|
||||
function DetailsText:render()
|
||||
self:setText()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local locale = self.props.locale
|
||||
local assetInfo = self.props.assetInfo or {}
|
||||
local partOfBundle = assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn == 1
|
||||
local multipleBundles = assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn > 1
|
||||
local showPremiumIcon = GetFFlagLuaPremiumCatalogIGIAB() and assetInfo.premiumPricing ~= nil
|
||||
local premiumIconPadding = showPremiumIcon and (UIBloxIconSize.Regular + PREMIUM_ICON_PADDING) or 0
|
||||
local noticeKey = nil
|
||||
if multipleBundles then
|
||||
noticeKey = Constants.NotificationKeys.MultipleBundleNoticeKey
|
||||
elseif partOfBundle then
|
||||
noticeKey = Constants.NotificationKeys.SingleBundleNoticeKey
|
||||
elseif assetInfo.isLimited then
|
||||
noticeKey = Constants.NotificationKeys.LimitedItemNoticeKey
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Position = viewMapping.DetailsFramePosition,
|
||||
Size = UDim2.new(1, 0, 0, ASSET_NAME_SIZE + CREATOR_SIZE + FAVORITES_SIZE + NOTIFICATION_SIZE),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 1,
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Top,
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
}),
|
||||
Notification = noticeKey and Roact.createElement(Notification, {
|
||||
noticeKey = noticeKey,
|
||||
}),
|
||||
AssetName = GetFFlagLuaPremiumCatalogIGIAB() and Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, -10, 0, ASSET_NAME_SIZE),
|
||||
LayoutOrder = 1,
|
||||
}, {
|
||||
PremiumIcon = showPremiumIcon and Roact.createElement("ImageLabel", Cryo.Dictionary.join(PremiumIcon, {
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
Position = UDim2.new(0, 0, 0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, UIBloxIconSize.Regular, 0, UIBloxIconSize.Regular),
|
||||
ImageColor3 = Color3.new(1, 1, 1),
|
||||
})) or nil,
|
||||
Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0, premiumIconPadding, 0, 0),
|
||||
Size = UDim2.new(1, premiumIconPadding, 0, ASSET_NAME_SIZE),
|
||||
LayoutOrder = 1,
|
||||
Text = self.name or "",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
TextScaled = true,
|
||||
TextSize = TITLE_TEXT_SIZE,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextColor3 = Colors.White,
|
||||
}, {
|
||||
UITextSizeConstraint = Roact.createElement("UITextSizeConstraint", {
|
||||
MaxTextSize = 32,
|
||||
})
|
||||
}),
|
||||
}) or Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, -10, 0, ASSET_NAME_SIZE),
|
||||
LayoutOrder = 1,
|
||||
Text = self.name or "",
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
TextScaled = true,
|
||||
TextSize = TITLE_TEXT_SIZE,
|
||||
Font = Enum.Font.GothamBold,
|
||||
TextColor3 = Colors.White,
|
||||
}, {
|
||||
UITextSizeConstraint = Roact.createElement("UITextSizeConstraint", {
|
||||
MaxTextSize = 32,
|
||||
})
|
||||
}),
|
||||
CreatorLabel = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, CREATOR_SIZE),
|
||||
LayoutOrder = 2,
|
||||
Text = RobloxTranslator:FormatByKeyForLocale(BY_KEY, locale, { CREATOR = self.creator }),
|
||||
Font = Enum.Font.Gotham,
|
||||
TextScaled = false,
|
||||
TextSize = TEXT_SIZE_SMALL,
|
||||
TextColor3 = Colors.White,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
}),
|
||||
FavoriteContainer = Roact.createElement(Favorites),
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
view = state.view,
|
||||
locale = state.locale,
|
||||
assetInfo = state.assets[assetId],
|
||||
bundleInfo = state.bundles,
|
||||
}
|
||||
end
|
||||
)(DetailsText)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local DetailsText = require(script.Parent.DetailsText)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(DetailsText, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Url = require(CorePackages.AppTempCommon.LuaApp.Http.Url)
|
||||
local UtilityFunctions = require(InspectAndBuyFolder.UtilityFunctions)
|
||||
|
||||
local GetFFlagUseThumbnailUrl = require(game:GetService("CoreGui").RobloxGui.Modules.Common.Flags.GetFFlagUseThumbnailUrl)
|
||||
|
||||
local DetailsThumbnail = Roact.PureComponent:extend("DetailsThumbnail")
|
||||
|
||||
local function isPartOfBundle(assetInfo)
|
||||
return assetInfo and assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn == 1
|
||||
end
|
||||
|
||||
function DetailsThumbnail:getUrl()
|
||||
local assetInfo = self.props.assetInfo
|
||||
local detailsInformation = self.props.detailsInformation
|
||||
local bundles = self.props.bundles
|
||||
local partOfBundle = isPartOfBundle(assetInfo)
|
||||
local url = ""
|
||||
|
||||
if partOfBundle then
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
if bundles[bundleId] then
|
||||
url = GetFFlagUseThumbnailUrl() and "rbxthumb://type=Outfit&id=" .. bundles[bundleId].costumeId .. "&w=420&h=420" or Url.BASE_URL_SECURE .."outfit-thumbnail/image?userOutfitId=" ..bundles[bundleId].costumeId
|
||||
.."&width=420&height=420&format=png"
|
||||
end
|
||||
else
|
||||
local assetId = detailsInformation.assetId
|
||||
url = GetFFlagUseThumbnailUrl() and "rbxthumb://type=Asset&id=" .. assetId .. "&w=420&h=420" or Url.BASE_URL_SECURE .."asset-thumbnail/image?assetId=" ..assetId .."&width=420&height=420&format=png"
|
||||
end
|
||||
|
||||
return url
|
||||
end
|
||||
|
||||
function DetailsThumbnail:render()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local detailsInformation = self.props.detailsInformation
|
||||
local tryingOnInfo = self.props.tryingOnInfo
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Position = viewMapping.DetailsThumbnailFramePosition,
|
||||
Size = UDim2.new(1, 0, 0, 300),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 2,
|
||||
Visible = not tryingOnInfo.tryingOn,
|
||||
}, {
|
||||
Thumbnail = detailsInformation.viewingDetails and Roact.createElement("ImageLabel", {
|
||||
Position = viewMapping.DetailsThumbnailPosition,
|
||||
AnchorPoint = viewMapping.DetailsThumbnailAnchorPoint,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Image = self:getUrl(),
|
||||
}, {
|
||||
AspectRatioConstraint = Roact.createElement("UIAspectRatioConstraint", {
|
||||
AspectRatio = 1,
|
||||
AspectType = Enum.AspectType.FitWithinMaxSize,
|
||||
DominantAxis = viewMapping.DetailsThumbnailARDominantAxis,
|
||||
}),
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
view = state.view,
|
||||
tryingOnInfo = state.tryingOnInfo,
|
||||
detailsInformation = state.detailsInformation,
|
||||
assetInfo = state.assets[assetId],
|
||||
bundles = state.bundles,
|
||||
}
|
||||
end
|
||||
)(DetailsThumbnail)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local DetailsThumbnail = require(script.Parent.DetailsThumbnail)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(DetailsThumbnail, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local UtilityFunctions = require(InspectAndBuyFolder.UtilityFunctions)
|
||||
local GetAssetFavoriteCount = require(InspectAndBuyFolder.Thunks.GetAssetFavoriteCount)
|
||||
local GetBundleFavoriteCount = require(InspectAndBuyFolder.Thunks.GetBundleFavoriteCount)
|
||||
local GotCurrentFavoriteCount = require(InspectAndBuyFolder.Selectors.GotCurrentFavoriteCount)
|
||||
local getSelectionImageObjectRegular = require(InspectAndBuyFolder.getSelectionImageObjectRegular)
|
||||
|
||||
local FAVORITES_SIZE = 16
|
||||
local FAVORITE_IMAGE_FILLED = "rbxasset://textures/ui/InspectMenu/ico_favorite.png"
|
||||
|
||||
local Favorites = Roact.PureComponent:extend("Favorites")
|
||||
|
||||
--[[
|
||||
Sets the variables used to display text. If a bundle is
|
||||
being shown, they will display the bundle's data instead.
|
||||
]]
|
||||
function Favorites:setText()
|
||||
local assetInfo = self.props.assetInfo or {}
|
||||
local partOfBundle = assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn > 0
|
||||
local bundleInfo = self.props.bundleInfo
|
||||
|
||||
if partOfBundle then
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
if bundleInfo[bundleId] then
|
||||
self.numFavorites = bundleInfo[bundleId].numFavorites
|
||||
end
|
||||
elseif assetInfo then
|
||||
self.numFavorites = assetInfo.numFavorites
|
||||
end
|
||||
end
|
||||
|
||||
function Favorites:init()
|
||||
self.numFavorites = ""
|
||||
self.selectionImageObject = getSelectionImageObjectRegular()
|
||||
end
|
||||
|
||||
function Favorites:willUpdate(nextProps)
|
||||
local getAssetFavoriteCount = self.props.getAssetFavoriteCount
|
||||
local getBundleFavoriteCount = self.props.getBundleFavoriteCount
|
||||
|
||||
-- We need to check if this asset is in any bundles, so that web call
|
||||
-- needs to be completed first.
|
||||
if nextProps.assetInfo and nextProps.assetInfo.bundlesAssetIsIn then
|
||||
local assetInfo = nextProps.assetInfo
|
||||
local partOfBundle = #assetInfo.bundlesAssetIsIn > 0
|
||||
local gotCurrentFavoriteCount = nextProps.gotCurrentFavoriteCount
|
||||
|
||||
if not gotCurrentFavoriteCount then
|
||||
coroutine.wrap(function()
|
||||
if not partOfBundle then
|
||||
getAssetFavoriteCount(assetInfo.assetId)
|
||||
else
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
getBundleFavoriteCount(bundleId)
|
||||
end
|
||||
end)()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Favorites:render()
|
||||
self:setText()
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, FAVORITES_SIZE),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 3,
|
||||
}, {
|
||||
FavoriteIcon = Roact.createElement("ImageButton", {
|
||||
Size = UDim2.new(0, FAVORITES_SIZE, 0, FAVORITES_SIZE),
|
||||
BackgroundTransparency = 1,
|
||||
Image = FAVORITE_IMAGE_FILLED,
|
||||
SelectionImageObject = self.selectionImageObject,
|
||||
}),
|
||||
FavoriteText = Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(1, -FAVORITES_SIZE, 1, 0),
|
||||
Position = UDim2.new(0, FAVORITES_SIZE + 5, 0, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Text = self.numFavorites or "",
|
||||
Font = Enum.Font.Gotham,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextSize = 12,
|
||||
TextColor3 = Color3.new(1, 1, 1),
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
function Favorites:shouldUpdate(nextProps)
|
||||
if not nextProps.detailsInformation.viewingDetails then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
detailsInformation = state.detailsInformation,
|
||||
assetInfo = state.assets[assetId],
|
||||
bundleInfo = state.bundles,
|
||||
gotCurrentFavoriteCount = GotCurrentFavoriteCount(state),
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
getAssetFavoriteCount = function(assetId)
|
||||
dispatch(GetAssetFavoriteCount(assetId))
|
||||
end,
|
||||
getBundleFavoriteCount = function(bundleId)
|
||||
dispatch(GetBundleFavoriteCount(bundleId))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(Favorites)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local Favorites = require(script.Parent.Favorites)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(Favorites, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local CreateFavoriteForAsset = require(InspectAndBuyFolder.Thunks.CreateFavoriteForAsset)
|
||||
local DeleteFavoriteForAsset = require(InspectAndBuyFolder.Thunks.DeleteFavoriteForAsset)
|
||||
local CreateFavoriteForBundle = require(InspectAndBuyFolder.Thunks.CreateFavoriteForBundle)
|
||||
local DeleteFavoriteForBundle = require(InspectAndBuyFolder.Thunks.DeleteFavoriteForBundle)
|
||||
local GetFavoriteForAsset = require(InspectAndBuyFolder.Thunks.GetFavoriteForAsset)
|
||||
local GetFavoriteForBundle = require(InspectAndBuyFolder.Thunks.GetFavoriteForBundle)
|
||||
local GotFavoriteForDetailsItem = require(InspectAndBuyFolder.Selectors.GotFavoriteForDetailsItem)
|
||||
local IsDetailsItemPartOfBundle = require(InspectAndBuyFolder.Selectors.IsDetailsItemPartOfBundle)
|
||||
local GetIsFavorite = require(InspectAndBuyFolder.Selectors.GetIsFavorite)
|
||||
local UtilityFunctions = require(InspectAndBuyFolder.UtilityFunctions)
|
||||
local getSelectionImageObjectRounded = require(InspectAndBuyFolder.getSelectionImageObjectRounded)
|
||||
|
||||
local FAVORITE_IMAGE_FILLED = "rbxasset://textures/ui/InspectMenu/ico_favorite.png"
|
||||
local FAVORITE_IMAGE_NOT_FILLED = "rbxasset://textures/ui/InspectMenu/ico_favorite_off.png"
|
||||
local ROBLOX_CREATOR_ID = "1"
|
||||
|
||||
local FavoritesButton = Roact.PureComponent:extend("FavoritesButton")
|
||||
|
||||
function FavoritesButton:init()
|
||||
self.selectedImage = getSelectionImageObjectRounded()
|
||||
end
|
||||
|
||||
function FavoritesButton:willUpdate(nextProps)
|
||||
local gotFavoriteForDetailsItem = nextProps.gotFavoriteForDetailsItem
|
||||
local getFavoriteForAsset = self.props.getFavoriteForAsset
|
||||
local getFavoriteForBundle = self.props.getFavoriteForBundle
|
||||
|
||||
if nextProps.assetInfo and nextProps.assetInfo.bundlesAssetIsIn and not gotFavoriteForDetailsItem then
|
||||
local assetInfo = nextProps.assetInfo
|
||||
local partOfBundle = #assetInfo.bundlesAssetIsIn > 0
|
||||
|
||||
coroutine.wrap(function()
|
||||
if not partOfBundle then
|
||||
getFavoriteForAsset(assetInfo.assetId)
|
||||
else
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
getFavoriteForBundle(bundleId)
|
||||
end
|
||||
end)()
|
||||
end
|
||||
end
|
||||
|
||||
function FavoritesButton:render()
|
||||
local isFavorited = self.props.isFavorited
|
||||
local createFavoriteForAsset = self.props.createFavoriteForAsset
|
||||
local deleteFavoriteForAsset = self.props.deleteFavoriteForAsset
|
||||
local createFavoriteForBundle = self.props.createFavoriteForBundle
|
||||
local deleteFavoriteForBundle = self.props.deleteFavoriteForBundle
|
||||
local isDetailsItemPartOfBundle = self.props.isDetailsItemPartOfBundle
|
||||
local favoriteButtonRef = self.props.favoriteButtonRef
|
||||
local assetInfo = self.props.assetInfo
|
||||
local creatorId = assetInfo and assetInfo.creatorId or 0
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, 44, 0, 44),
|
||||
LayoutOrder = 1,
|
||||
Image = "rbxasset://textures/ui/InspectMenu/Button_outline.png",
|
||||
ImageColor3 = Colors.White,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(5, 5, 120, 20),
|
||||
SelectionImageObject = self.selectedImage,
|
||||
-- Users can only favorite Roblox created items, otherwise they'll be captcha'd. We do not support captchas in game.
|
||||
Visible = creatorId == ROBLOX_CREATOR_ID,
|
||||
[Roact.Ref] = favoriteButtonRef,
|
||||
[Roact.Event.Activated] = function()
|
||||
if isFavorited then
|
||||
if isDetailsItemPartOfBundle then
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
deleteFavoriteForBundle(bundleId)
|
||||
else
|
||||
deleteFavoriteForAsset(assetInfo.assetId)
|
||||
end
|
||||
else
|
||||
if isDetailsItemPartOfBundle then
|
||||
local bundleId = UtilityFunctions.getBundleId(assetInfo)
|
||||
createFavoriteForBundle(bundleId)
|
||||
else
|
||||
createFavoriteForAsset(assetInfo.assetId)
|
||||
end
|
||||
end
|
||||
end,
|
||||
}, {
|
||||
FavoriteIcon = Roact.createElement("ImageLabel", {
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0, 26, 0, 26),
|
||||
Position = UDim2.new(0, 22, 0, 22),
|
||||
Image = isFavorited and FAVORITE_IMAGE_FILLED or FAVORITE_IMAGE_NOT_FILLED,
|
||||
ImageColor3 = Colors.White,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
view = state.view,
|
||||
assetInfo = state.assets[assetId],
|
||||
bundleInfo = state.bundles,
|
||||
gotFavoriteForDetailsItem = GotFavoriteForDetailsItem(state),
|
||||
isFavorited = GetIsFavorite(state),
|
||||
isDetailsItemPartOfBundle = IsDetailsItemPartOfBundle(state),
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
createFavoriteForAsset = function(assetId)
|
||||
dispatch(CreateFavoriteForAsset(assetId))
|
||||
end,
|
||||
deleteFavoriteForAsset = function(assetId)
|
||||
dispatch(DeleteFavoriteForAsset(assetId))
|
||||
end,
|
||||
createFavoriteForBundle = function(bundleId)
|
||||
dispatch(CreateFavoriteForBundle(bundleId))
|
||||
end,
|
||||
deleteFavoriteForBundle = function(bundleId)
|
||||
dispatch(DeleteFavoriteForBundle(bundleId))
|
||||
end,
|
||||
getFavoriteForAsset = function(assetId)
|
||||
dispatch(GetFavoriteForAsset(assetId))
|
||||
end,
|
||||
getFavoriteForBundle = function(bundleId)
|
||||
dispatch(GetFavoriteForBundle(bundleId))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(FavoritesButton)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local FavoritesButton = require(script.Parent.FavoritesButton)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(FavoritesButton, {
|
||||
FavoritesButtonRef = Roact.createRef(),
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local PlayerNameLabel = require(InspectAndBuyFolder.Components.PlayerNameLabel)
|
||||
local AvatarHeadShot = require(InspectAndBuyFolder.Components.AvatarHeadShot)
|
||||
local AssetList = require(InspectAndBuyFolder.Components.AssetList)
|
||||
|
||||
local InitialView = Roact.PureComponent:extend("InitialView")
|
||||
|
||||
function InitialView:render()
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local detailsInformation = self.props.detailsInformation
|
||||
|
||||
return Roact.createElement("ScrollingFrame", {
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0, viewMapping.BorderPaddingSize, 0, 10),
|
||||
Size = UDim2.new(1, -(2 * viewMapping.BorderPaddingSize), 1, -36),
|
||||
ScrollBarThickness = 0,
|
||||
ScrollingDirection = Enum.ScrollingDirection.Y,
|
||||
Visible = not detailsInformation.viewingDetails,
|
||||
Selectable = false,
|
||||
}, {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
Padding = UDim.new(0, 15),
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Left,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
Top = Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, viewMapping.TopSizeY),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 2
|
||||
}, {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
Padding = UDim.new(0, 10),
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
}),
|
||||
AvatarHeadShot = Roact.createElement(AvatarHeadShot),
|
||||
PlayerNameLabel = Roact.createElement(PlayerNameLabel),
|
||||
}),
|
||||
AssetList = Roact.createElement(AssetList),
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
view = state.view,
|
||||
detailsInformation = state.detailsInformation,
|
||||
}
|
||||
end
|
||||
)(InitialView)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local InitialView = require(script.Parent.InitialView)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(InitialView, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local LocalizationService = game:GetService("LocalizationService")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local MarketplaceService = game:GetService("MarketplaceService")
|
||||
local Players = game:GetService("Players")
|
||||
local MouseIconOverrideService = require(CorePackages.InGameServices.MouseIconOverrideService)
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Symbol = require(CorePackages.Symbol)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetDetailsInformation = require(InspectAndBuyFolder.Actions.SetDetailsInformation)
|
||||
local SetTryingOnInfo = require(InspectAndBuyFolder.Actions.SetTryingOnInfo)
|
||||
local Thunk = require(InspectAndBuyFolder.Thunk)
|
||||
local Network = require(InspectAndBuyFolder.Services.Network)
|
||||
local Analytics = require(InspectAndBuyFolder.Services.Analytics)
|
||||
local InspectAndBuyReducer = require(InspectAndBuyFolder.Reducers.InspectAndBuyReducer)
|
||||
local Container = require(InspectAndBuyFolder.Components.Container)
|
||||
local Constants = require(InspectAndBuyFolder.Constants)
|
||||
local CompactView = require(InspectAndBuyFolder.CompactView)
|
||||
local WideView = require(InspectAndBuyFolder.WideView)
|
||||
local WideLandscapeView = require(InspectAndBuyFolder.WideLandscapeView)
|
||||
local SetGamepadEnabled = require(InspectAndBuyFolder.Actions.SetGamepadEnabled)
|
||||
local SetView = require(InspectAndBuyFolder.Actions.SetView)
|
||||
local SetPlayerId = require(InspectAndBuyFolder.Actions.SetPlayerId)
|
||||
local SetPlayerName = require(InspectAndBuyFolder.Actions.SetPlayerName)
|
||||
local SetLocale = require(InspectAndBuyFolder.Actions.SetLocale)
|
||||
local SetItemBeingPurchased = require(InspectAndBuyFolder.Actions.SetItemBeingPurchased)
|
||||
local UpdateStoreId = require(InspectAndBuyFolder.Actions.UpdateStoreId)
|
||||
local GetAssetsFromHumanoidDescription = require(InspectAndBuyFolder.Thunks.GetAssetsFromHumanoidDescription)
|
||||
local UpdateOwnedStatus = require(InspectAndBuyFolder.Thunks.UpdateOwnedStatus)
|
||||
local GetCharacterModelFromUserId = require(InspectAndBuyFolder.Thunks.GetCharacterModelFromUserId)
|
||||
local GetPlayerName = require(InspectAndBuyFolder.Thunks.GetPlayerName)
|
||||
|
||||
local FFlagFixInspectAndBuyPerformFetch = require(InspectAndBuyFolder.Flags.FFlagFixInspectAndBuyPerformFetch)
|
||||
|
||||
local COMPACT_VIEW_MAX_WIDTH = 600
|
||||
local CURSOR_OVERRIDE_KEY = Symbol.named("OverrideCursorInspectMenu")
|
||||
local BACK_BUTTON_KEY = "BackButtonInspectMenu"
|
||||
|
||||
local InspectAndBuy = Roact.PureComponent:extend("InspectAndBuy")
|
||||
|
||||
function InspectAndBuy:pushMouseIconOverride()
|
||||
local input = UserInputService:GetLastInputType()
|
||||
local isGamepad = input.Name:find('Gamepad')
|
||||
|
||||
if isGamepad then
|
||||
MouseIconOverrideService.push(CURSOR_OVERRIDE_KEY, Enum.OverrideMouseIconBehavior.ForceHide)
|
||||
else
|
||||
MouseIconOverrideService.push(CURSOR_OVERRIDE_KEY, Enum.OverrideMouseIconBehavior.ForceShow)
|
||||
end
|
||||
end
|
||||
|
||||
function InspectAndBuy:popMouseIconOverride()
|
||||
MouseIconOverrideService.pop(CURSOR_OVERRIDE_KEY)
|
||||
end
|
||||
|
||||
function InspectAndBuy:configureInputType(lastInputType)
|
||||
local input = lastInputType or UserInputService:GetLastInputType()
|
||||
local isGamepad = input.Name:find('Gamepad')
|
||||
|
||||
if isGamepad then
|
||||
self.state.store:dispatch(SetGamepadEnabled(true))
|
||||
else
|
||||
self.state.store:dispatch(SetGamepadEnabled(false))
|
||||
end
|
||||
end
|
||||
|
||||
function InspectAndBuy:updateView()
|
||||
local viewportSize = workspace.CurrentCamera.ViewportSize
|
||||
local view = Constants.View.Wide
|
||||
|
||||
if viewportSize.X < COMPACT_VIEW_MAX_WIDTH then
|
||||
view = Constants.View.Compact
|
||||
elseif viewportSize.X > viewportSize.Y and viewportSize.X < 800 then
|
||||
view = Constants.View.WideLandscape
|
||||
end
|
||||
|
||||
self.state.store:dispatch(SetView(view))
|
||||
end
|
||||
|
||||
function InspectAndBuy:init()
|
||||
local playerId = self.props.playerId
|
||||
local playerName = self.props.playerName
|
||||
local ctx = self.props.ctx
|
||||
self.connections = {}
|
||||
self.network = Network.new()
|
||||
self.analytics = Analytics.new(playerId, ctx)
|
||||
self.humanoidDescription = self.props.humanoidDescription
|
||||
|
||||
self.analytics.reportOpenInspectMenu()
|
||||
|
||||
self.state = {
|
||||
store = Rodux.Store.new(InspectAndBuyReducer, {}, {
|
||||
Thunk.middleware({
|
||||
[Network] = self.network,
|
||||
[Analytics] = self.analytics,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
|
||||
if FFlagFixInspectAndBuyPerformFetch then
|
||||
self.state.store:dispatch(UpdateStoreId())
|
||||
end
|
||||
|
||||
self._context[Constants.View.Compact] = CompactView
|
||||
self._context[Constants.View.Wide] = WideView
|
||||
self._context[Constants.View.WideLandscape] = WideLandscapeView
|
||||
|
||||
self.state.store:dispatch(SetPlayerName(playerName))
|
||||
self.state.store:dispatch(SetLocale(LocalizationService.RobloxLocaleId))
|
||||
|
||||
if playerId then
|
||||
self.state.store:dispatch(SetPlayerId(playerId))
|
||||
end
|
||||
|
||||
if self.humanoidDescription then
|
||||
self.state.store:dispatch(GetAssetsFromHumanoidDescription(self.humanoidDescription, false))
|
||||
end
|
||||
end
|
||||
|
||||
function InspectAndBuy:didMount()
|
||||
local playerId = self.props.playerId
|
||||
self.isMounted = true
|
||||
self:updateView()
|
||||
self:bindButtonB()
|
||||
self:configureInputType()
|
||||
self:pushMouseIconOverride()
|
||||
|
||||
local inputTypeChangedListener = UserInputService.LastInputTypeChanged:Connect(function(lastInputType)
|
||||
self:configureInputType(lastInputType)
|
||||
end)
|
||||
|
||||
local viewportSizeListener = workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):connect(function()
|
||||
self:updateView()
|
||||
end)
|
||||
|
||||
local localeListener = LocalizationService:GetPropertyChangedSignal("RobloxLocaleId"):connect(function()
|
||||
self.state.store:dispatch(SetLocale(LocalizationService.RobloxLocaleId))
|
||||
end)
|
||||
|
||||
local menuOpenedConnection = GuiService.MenuOpened:Connect(function()
|
||||
self:popMouseIconOverride()
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end)
|
||||
|
||||
local menuClosedConnection = GuiService.MenuClosed:Connect(function()
|
||||
self:pushMouseIconOverride()
|
||||
end)
|
||||
|
||||
-- Update the owned status of an item if a user purchases it.
|
||||
local marketplaceServicePurchaseFinishedListener =
|
||||
MarketplaceService.PromptPurchaseFinished:Connect(function(player, itemId, isPurchased)
|
||||
|
||||
local purchasedInformation = self.state.store:getState().itemBeingPurchased
|
||||
|
||||
if isPurchased and tostring(itemId) == purchasedInformation.itemId then
|
||||
self.analytics.reportPurchaseSuccess(purchasedInformation.itemType, purchasedInformation.itemId)
|
||||
self.state.store:dispatch(UpdateOwnedStatus(purchasedInformation.itemId, purchasedInformation.itemType))
|
||||
end
|
||||
self.state.store:dispatch(SetItemBeingPurchased(nil, nil))
|
||||
end)
|
||||
|
||||
local storeChangedConnection = self.state.store.changed:connect(function(state, oldState)
|
||||
self:update(state, oldState)
|
||||
end)
|
||||
|
||||
table.insert(self.connections, viewportSizeListener)
|
||||
table.insert(self.connections, localeListener)
|
||||
table.insert(self.connections, menuOpenedConnection)
|
||||
table.insert(self.connections, menuClosedConnection)
|
||||
table.insert(self.connections, inputTypeChangedListener)
|
||||
table.insert(self.connections, marketplaceServicePurchaseFinishedListener)
|
||||
table.insert(self.connections, storeChangedConnection)
|
||||
|
||||
local localUserId = Players.LocalPlayer.UserId
|
||||
self.state.store:dispatch(GetCharacterModelFromUserId(localUserId, true, function(localPlayerModel)
|
||||
if self and self.isMounted then
|
||||
self.localPlayerModel = localPlayerModel
|
||||
local humanoidDescriptionForLocalPlayer = localPlayerModel.Humanoid.HumanoidDescription
|
||||
|
||||
if humanoidDescriptionForLocalPlayer then
|
||||
self.state.store:dispatch(GetAssetsFromHumanoidDescription(humanoidDescriptionForLocalPlayer, true))
|
||||
end
|
||||
|
||||
self:setState({
|
||||
obtainedLocalPlayerModel = true,
|
||||
})
|
||||
end
|
||||
end))
|
||||
|
||||
if playerId then
|
||||
self.state.store:dispatch(GetCharacterModelFromUserId(playerId, false, function(playerModel)
|
||||
if self and self.isMounted then
|
||||
self.playerModel = playerModel
|
||||
local humanoidDescription = self.playerModel.Humanoid.HumanoidDescription
|
||||
|
||||
if humanoidDescription then
|
||||
self.state.store:dispatch(GetAssetsFromHumanoidDescription(humanoidDescription, false))
|
||||
end
|
||||
|
||||
self:setState({
|
||||
obtainedPlayerModel = true,
|
||||
})
|
||||
end
|
||||
end))
|
||||
|
||||
self.state.store:dispatch(GetPlayerName(playerId))
|
||||
end
|
||||
end
|
||||
|
||||
function InspectAndBuy:update(newState, oldState)
|
||||
if not newState.visible and oldState.visible then
|
||||
ContextActionService:UnbindCoreAction(BACK_BUTTON_KEY)
|
||||
elseif newState.visible and not oldState.visible then
|
||||
self:bindButtonB()
|
||||
end
|
||||
end
|
||||
|
||||
function InspectAndBuy:willUnmount()
|
||||
self.isMounted = false
|
||||
for _, connection in pairs(self.connections) do
|
||||
connection:disconnect()
|
||||
end
|
||||
|
||||
ContextActionService:UnbindCoreAction(BACK_BUTTON_KEY)
|
||||
self:popMouseIconOverride()
|
||||
end
|
||||
|
||||
function InspectAndBuy:render()
|
||||
local localPlayerModel = self.localPlayerModel
|
||||
|
||||
return Roact.createElement(RoactRodux.StoreProvider, {
|
||||
store = self.state.store,
|
||||
}, {
|
||||
Container = Roact.createElement(Container, {
|
||||
localPlayerModel = localPlayerModel,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
function InspectAndBuy:bindButtonB()
|
||||
ContextActionService:BindCoreAction(BACK_BUTTON_KEY, function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End and inputObject.KeyCode == Enum.KeyCode.ButtonB then
|
||||
local viewingDetails = self.state.store:getState().detailsInformation.viewingDetails
|
||||
|
||||
if viewingDetails then
|
||||
self.state.store:dispatch(SetDetailsInformation(false, nil))
|
||||
self.state.store:dispatch(SetTryingOnInfo(false, nil))
|
||||
else
|
||||
GuiService:CloseInspectMenu()
|
||||
end
|
||||
elseif inputState == Enum.UserInputState.Begin
|
||||
and inputObject.KeyCode == Enum.KeyCode.Escape or inputObject.KeyCode == Enum.KeyCode.ButtonStart then
|
||||
-- Close the Inspect Menu, allow the ESC menu to open.
|
||||
GuiService:CloseInspectMenu()
|
||||
return Enum.ContextActionResult.Pass
|
||||
end
|
||||
|
||||
return Enum.ContextActionResult.Sink
|
||||
end,
|
||||
false, Enum.KeyCode.ButtonB, Enum.KeyCode.Escape, Enum.KeyCode.ButtonStart)
|
||||
end
|
||||
|
||||
return InspectAndBuy
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local InspectAndBuy = require(script.Parent.InspectAndBuy)
|
||||
|
||||
local mockModel = require(InspectAndBuyFolder.Test.getMockModel)()
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(InspectAndBuy, {
|
||||
localPlayerModel = mockModel,
|
||||
ctx = "tests",
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local RobloxTranslator = require(CoreGui.RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local NO_INVENTORY_KEY = "InGame.InspectMenu.Description.NoInventoryNotice"
|
||||
|
||||
local NoInventoryNotice = Roact.PureComponent:extend("NoInventoryNotice")
|
||||
|
||||
function NoInventoryNotice:render()
|
||||
local locale = self.props.locale
|
||||
local assets = self.props.assets
|
||||
local isLoaded = self.props.isLoaded
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Visible = next(assets) == nil and isLoaded,
|
||||
}, {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
Padding = UDim.new(0, 15),
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
}),
|
||||
NotWearingImage = Roact.createElement("ImageLabel", {
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0.25, 0, 0.25, 0),
|
||||
LayoutOrder = 1,
|
||||
Image = "rbxasset://textures/ui/InspectMenu/ico_isnt-wearing.png",
|
||||
ImageColor3 = Colors.White,
|
||||
}, {
|
||||
UIAspectRatioConstraint = Roact.createElement("UIAspectRatioConstraint", {
|
||||
AspectRatio = 1,
|
||||
})
|
||||
}),
|
||||
NotWearingText = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0.8, 0, 0.3, 0),
|
||||
LayoutOrder = 2,
|
||||
Font = Enum.Font.Gotham,
|
||||
Text = RobloxTranslator:FormatByKeyForLocale(NO_INVENTORY_KEY, locale),
|
||||
TextScaled = true,
|
||||
TextColor3 = Colors.White,
|
||||
TextSize = 20,
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
TextWrapped = true,
|
||||
}, {
|
||||
UITextSizeConstraint = Roact.createElement("UITextSizeConstraint", {
|
||||
MaxTextSize = 20,
|
||||
})
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
assets = state.assets,
|
||||
locale = state.locale,
|
||||
isLoaded = state.isLoaded,
|
||||
}
|
||||
end
|
||||
)(NoInventoryNotice)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local NoInventoryNotice = require(script.Parent.NoInventoryNotice)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(NoInventoryNotice, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local RobloxTranslator = require(CoreGui.RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local Notification = Roact.PureComponent:extend("Notification")
|
||||
|
||||
function Notification:render()
|
||||
local locale = self.props.locale
|
||||
local noticeKey = self.props.noticeKey
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, -10, 0, 45),
|
||||
LayoutOrder = 0,
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
} , {
|
||||
UIListLayout = Roact.createElement("UIListLayout", {
|
||||
Padding = UDim.new(0, 10),
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
}),
|
||||
ExclamationPoint = Roact.createElement("ImageLabel", {
|
||||
Size = UDim2.new(0, 16, 0, 16),
|
||||
BackgroundTransparency = 1,
|
||||
Image = "rbxasset://textures/ui/InspectMenu/ico_alert_tilt.png",
|
||||
ImageColor3 = Colors.White,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
PartOfBundleText = Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(1, -30, 1, 0),
|
||||
TextColor3 = Colors.White,
|
||||
BorderSizePixel = 0,
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 2,
|
||||
TextWrapped = true,
|
||||
TextScaled = true,
|
||||
TextSize = 12,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.Gotham,
|
||||
Text = RobloxTranslator:FormatByKeyForLocale(noticeKey, locale),
|
||||
}, {
|
||||
UITextSizeConstraint = Roact.createElement("UITextSizeConstraint", {
|
||||
MaxTextSize = 16,
|
||||
})
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
locale = state.locale
|
||||
}
|
||||
end
|
||||
)(Notification)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local Notification = require(script.Parent.Notification)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(Notification, {
|
||||
noticeKey = "InGame.InspectMenu.Description.SingleBundleNotice",
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local RobloxTranslator = require(CoreGui.RobloxGui.Modules.RobloxTranslator)
|
||||
|
||||
local PlayerNameLabel = Roact.PureComponent:extend("PlayerNameLabel")
|
||||
|
||||
local INVENTORY_KEY = "InGame.InspectMenu.Label.Avatar"
|
||||
|
||||
function PlayerNameLabel:render()
|
||||
local locale = self.props.locale
|
||||
local view = self.props.view
|
||||
local viewMapping = self._context[view]
|
||||
local playerName = self.props.playerName
|
||||
local sizeX = viewMapping.AvatarHeadShotSize + (viewMapping.BorderPaddingSize * 2) + 10
|
||||
local playerNameText
|
||||
|
||||
if playerName == "" then
|
||||
playerNameText = ""
|
||||
else
|
||||
playerNameText = RobloxTranslator:FormatByKeyForLocale(INVENTORY_KEY, locale, { PLAYER_NAME = playerName })
|
||||
end
|
||||
|
||||
return Roact.createElement("TextLabel", {
|
||||
Size = UDim2.new(1, -sizeX, 0, viewMapping.TopSizeY),
|
||||
BackgroundTransparency = 1,
|
||||
TextColor3 = Colors.White,
|
||||
TextScaled = true,
|
||||
Text = playerNameText,
|
||||
TextSize = 36,
|
||||
Font = Enum.Font.GothamBlack,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
LayoutOrder = 2,
|
||||
}, {
|
||||
TextSizeConstraint = Roact.createElement("UITextSizeConstraint", {
|
||||
MaxTextSize = 36,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
view = state.view,
|
||||
playerName = state.playerName,
|
||||
locale = state.locale
|
||||
}
|
||||
end
|
||||
)(PlayerNameLabel)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local PlayerNameLabel = require(script.Parent.PlayerNameLabel)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(PlayerNameLabel, {})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxTranslator = require(CoreGui.RobloxGui.Modules.RobloxTranslator)
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Colors = require(InspectAndBuyFolder.Colors)
|
||||
local SetTryingOnInfo = require(InspectAndBuyFolder.Actions.SetTryingOnInfo)
|
||||
local TryOnItem = require(InspectAndBuyFolder.Thunks.TryOnItem)
|
||||
local getSelectionImageObjectRounded = require(InspectAndBuyFolder.getSelectionImageObjectRounded)
|
||||
|
||||
local TRY_ON_KEY = "InGame.InspectMenu.Action.TryOn"
|
||||
local TAKE_OFF_KEY = "InGame.InspectMenu.Action.TakeOff"
|
||||
local TEXT_SIZE = 16
|
||||
local BUTTON_PADDING = 10
|
||||
local ROBLOX_CREATOR_ID = "1"
|
||||
|
||||
local TryOnButton = Roact.PureComponent:extend("TryOnButton")
|
||||
|
||||
function TryOnButton:init()
|
||||
self.selectedImage = getSelectionImageObjectRounded()
|
||||
end
|
||||
|
||||
function TryOnButton:render()
|
||||
local tryOnItem = self.props.tryOnItem
|
||||
local takeOffItem = self.props.takeOffItem
|
||||
local tryingOn = self.props.tryingOnInfo.tryingOn
|
||||
local showTryOn = self.props.showTryOn
|
||||
local locale = self.props.locale
|
||||
local assetInfo = self.props.assetInfo
|
||||
local creatorId = assetInfo and assetInfo.creatorId or 0
|
||||
local partOfBundle = self.props.partOfBundle
|
||||
local bundleId = self.props.bundleId
|
||||
local tryOnButtonRef = self.props.tryOnButtonRef
|
||||
local sizeXAdjustment = creatorId == ROBLOX_CREATOR_ID and -32 or -BUTTON_PADDING / 2
|
||||
local tryOnTextKey
|
||||
|
||||
if tryingOn then
|
||||
tryOnTextKey = TAKE_OFF_KEY
|
||||
else
|
||||
tryOnTextKey = TRY_ON_KEY
|
||||
end
|
||||
|
||||
return Roact.createElement("ImageLabel", {
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(0.5, sizeXAdjustment, 0, 44),
|
||||
Visible = showTryOn,
|
||||
LayoutOrder = 2,
|
||||
Image = "rbxasset://textures/ui/InspectMenu/Button_outline.png",
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(5, 5, 120, 20),
|
||||
}, {
|
||||
SelectionGainedImage = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Visible = false,
|
||||
Image = "rbxasset://textures/ui/InspectMenu/Button_white.png",
|
||||
ImageColor3 = Colors.Pumice,
|
||||
}),
|
||||
TryOnTextKey = Roact.createElement("TextButton", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
Text = RobloxTranslator:FormatByKeyForLocale(tryOnTextKey, locale),
|
||||
Font = Enum.Font.Gotham,
|
||||
TextSize = TEXT_SIZE,
|
||||
TextColor3 = Colors.White,
|
||||
SelectionImageObject = self.selectedImage,
|
||||
ZIndex = 2,
|
||||
[Roact.Event.SelectionGained] = function(rbx)
|
||||
rbx.Parent.SelectionGainedImage.Visible = true
|
||||
rbx.TextColor3 = Colors.Carbon
|
||||
end,
|
||||
[Roact.Event.SelectionLost] = function(rbx)
|
||||
rbx.Parent.SelectionGainedImage.Visible = false
|
||||
rbx.TextColor3 = Colors.White
|
||||
end,
|
||||
[Roact.Ref] = tryOnButtonRef,
|
||||
[Roact.Event.Activated] = function()
|
||||
if tryingOn then
|
||||
takeOffItem()
|
||||
else
|
||||
tryOnItem(true, assetInfo.assetId, assetInfo.assetTypeId, partOfBundle, bundleId)
|
||||
end
|
||||
end,
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
locale = state.locale,
|
||||
view = state.view,
|
||||
assetInfo = state.assets[assetId],
|
||||
bundleInfo = state.bundles,
|
||||
tryingOnInfo = state.tryingOnInfo,
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
tryOnItem = function(tryingOn, assetId, assetTypeId, partOfBundle, bundleId)
|
||||
dispatch(TryOnItem(tryingOn, assetId, assetTypeId, partOfBundle, bundleId))
|
||||
end,
|
||||
takeOffItem = function()
|
||||
dispatch(SetTryingOnInfo(false, nil, nil))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(TryOnButton)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local TryOnButton = require(script.Parent.TryOnButton)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(TryOnButton, {
|
||||
tryOnButtonRef = Roact.createRef(),
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Cryo = require(CorePackages.Cryo)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local Roact = require(CorePackages.Roact)
|
||||
local RoactRodux = require(CorePackages.RoactRodux)
|
||||
local Constants = require(InspectAndBuyFolder.Constants)
|
||||
local AvatarViewport = require(InspectAndBuyFolder.Components.AvatarViewport)
|
||||
local GetHumanoidDescriptionFromCostumeId = require(InspectAndBuyFolder.Thunks.GetHumanoidDescriptionFromCostumeId)
|
||||
|
||||
local TryOnViewport = Roact.PureComponent:extend("TryOnViewport")
|
||||
|
||||
local function isPartOfBundle(assetInfo)
|
||||
return assetInfo and assetInfo.bundlesAssetIsIn and #assetInfo.bundlesAssetIsIn == 1
|
||||
end
|
||||
|
||||
function TryOnViewport:init()
|
||||
self.model = self.props.localPlayerModel
|
||||
self.model.HumanoidRootPart.Anchored = true
|
||||
self.humanoidDescriptions = {}
|
||||
self.humanoidDescriptionForLocalPlayer = self.model.Humanoid.HumanoidDescription
|
||||
self.state = {
|
||||
obtainedHumanoidDescriptions = {}
|
||||
}
|
||||
end
|
||||
|
||||
function TryOnViewport:didMount()
|
||||
self.isMounted = true
|
||||
end
|
||||
|
||||
function TryOnViewport:didUpdate(prevProps)
|
||||
local assetInfo = self.props.assetInfo
|
||||
local bundles = self.props.bundles
|
||||
local tryingOnInfo = self.props.tryingOnInfo
|
||||
local prevTryingOnInfo = prevProps.tryingOnInfo
|
||||
|
||||
if tryingOnInfo ~= prevTryingOnInfo and tryingOnInfo.tryingOn and isPartOfBundle(assetInfo) then
|
||||
local bundleId = assetInfo.bundlesAssetIsIn[1]
|
||||
local costumeId = bundles[bundleId].costumeId
|
||||
|
||||
-- Get the humanoid description of this bundle's costume.
|
||||
self.props.getHumanoidDescriptionFromCostumeId(costumeId, function(humanoidDescription)
|
||||
if self and self.isMounted then
|
||||
self.humanoidDescriptions[costumeId] = humanoidDescription
|
||||
self:setState({
|
||||
obtainedHumanoidDescriptions = Cryo.Dictionary.join(self.state.obtainedHumanoidDescriptions, {[costumeId] = true})
|
||||
})
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function TryOnViewport:render()
|
||||
local assetInfo = self.props.assetInfo
|
||||
local bundles = self.props.bundles
|
||||
local tryingOnInfo = self.props.tryingOnInfo
|
||||
local humanoidDescription = self.humanoidDescriptionForLocalPlayer:Clone()
|
||||
local visible = tryingOnInfo.tryingOn
|
||||
local setScrollingEnabled = self.props.setScrollingEnabled
|
||||
|
||||
if tryingOnInfo and tryingOnInfo.tryingOn then
|
||||
if isPartOfBundle(assetInfo) then
|
||||
local bundleId = assetInfo.bundlesAssetIsIn[1]
|
||||
local costumeId = bundles[bundleId].costumeId
|
||||
local costumeHumanoidDescription = self.humanoidDescriptions[costumeId]
|
||||
visible = self.state.obtainedHumanoidDescriptions[costumeId] == true
|
||||
if costumeHumanoidDescription then
|
||||
-- Overwrite the inspecter's assets with any asset from the costume.
|
||||
for _, value in pairs(Constants.HumanoidDescriptionAssetNames) do
|
||||
if tonumber(costumeHumanoidDescription[value]) and tostring(costumeHumanoidDescription[value]) ~= "0" then
|
||||
humanoidDescription[value] = costumeHumanoidDescription[value]
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
humanoidDescription[Constants.HumanoidDescriptionIdToName[tryingOnInfo.assetTypeId]] = tryingOnInfo.assetId
|
||||
end
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 0, 300),
|
||||
LayoutOrder = 2,
|
||||
Visible = visible,
|
||||
}, {
|
||||
AvatarViewport = Roact.createElement(AvatarViewport, {
|
||||
size = UDim2.new(0.75, 0, 1, 0),
|
||||
position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
humanoidDescription = humanoidDescription,
|
||||
model = self.model,
|
||||
visible = true,
|
||||
backgroundTransparency = 1,
|
||||
setScrollingEnabled = setScrollingEnabled,
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
function TryOnViewport:willUnmount()
|
||||
self.isMounted = false
|
||||
end
|
||||
|
||||
return RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
local assetId = state.detailsInformation.assetId
|
||||
|
||||
return {
|
||||
assetInfo = state.assets[assetId],
|
||||
bundles = state.bundles,
|
||||
tryingOnInfo = state.tryingOnInfo,
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
getHumanoidDescriptionFromCostumeId = function(costumeId, callback)
|
||||
return dispatch(GetHumanoidDescriptionFromCostumeId(costumeId, callback))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(TryOnViewport)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Roact = require(CorePackages.Roact)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local TestContainer = require(InspectAndBuyFolder.Test.TestContainer)
|
||||
|
||||
local TryOnViewport = require(script.Parent.TryOnViewport)
|
||||
|
||||
local mockModel = require(InspectAndBuyFolder.Test.getMockModel)()
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = Roact.createElement(TestContainer, nil, {
|
||||
Roact.createElement(TryOnViewport, {
|
||||
localPlayerModel = mockModel,
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,88 @@
|
||||
local Constants = {
|
||||
NotificationKeys = {
|
||||
MultipleBundleNoticeKey = "InGame.InspectMenu.Description.MultipleBundlesNotice",
|
||||
SingleBundleNoticeKey = "InGame.InspectMenu.Description.SingleBundleNotice",
|
||||
LimitedItemNoticeKey = "InGame.InspectMenu.Description.LimitedNotice",
|
||||
},
|
||||
|
||||
ItemType = {
|
||||
Asset = "Asset",
|
||||
Bundle = "Bundle",
|
||||
},
|
||||
|
||||
View = {
|
||||
Compact = 0,
|
||||
Wide = 1,
|
||||
WideLandscape = 2,
|
||||
},
|
||||
|
||||
HumanoidDescriptionAssetNames = {
|
||||
BackAccessory = "BackAccessory",
|
||||
FaceAccessory = "FaceAccessory",
|
||||
FrontAccessory = "FrontAccessory",
|
||||
HairAccessory = "HairAccessory",
|
||||
HatAccessory = "HatAccessory",
|
||||
NeckAccessory = "NeckAccessory",
|
||||
ShouldersAccessory = "ShouldersAccessory",
|
||||
WaistAccessory = "WaistAccessory",
|
||||
Face = "Face",
|
||||
Head = "Head",
|
||||
LeftArm = "LeftArm",
|
||||
LeftLeg = "LeftLeg",
|
||||
RightArm = "RightArm",
|
||||
RightLeg = "RightLeg",
|
||||
Torso = "Torso",
|
||||
GraphicTShirt = "GraphicTShirt",
|
||||
Pants = "Pants",
|
||||
Shirt = "Shirt",
|
||||
ClimbAnimation = "ClimbAnimation",
|
||||
FallAnimation = "FallAnimation",
|
||||
IdleAnimation = "IdleAnimation",
|
||||
JumpAnimation = "JumpAnimation",
|
||||
RunAnimation = "RunAnimation",
|
||||
SwimAnimation = "SwimAnimation",
|
||||
WalkAnimation = "WalkAnimation",
|
||||
},
|
||||
|
||||
HumanoidDescriptionIdToName = {
|
||||
["2"] = "Shirt",
|
||||
["8"] = "HatAccessory",
|
||||
["41"] = "HairAccessory",
|
||||
["42"] = "FaceAccessory",
|
||||
["43"] = "NeckAccessory",
|
||||
["44"] = "ShouldersAccessory",
|
||||
["45"] = "FrontAccessory",
|
||||
["46"] = "BackAccessory",
|
||||
["47"] = "WaistAccessory",
|
||||
["11"] = "Shirt",
|
||||
["12"] = "Pants",
|
||||
["19"] = "Gear",
|
||||
["17"] = "Head",
|
||||
["18"] = "Face",
|
||||
["27"] = "Torso",
|
||||
["28"] = "RightArm",
|
||||
["29"] = "LeftArm",
|
||||
["30"] = "LeftLeg",
|
||||
["31"] = "RightLeg",
|
||||
["48"] = "ClimbAnimation",
|
||||
["50"] = "FallAnimation",
|
||||
["51"] = "IdleAnimation",
|
||||
["52"] = "JumpAnimation",
|
||||
["53"] = "RunAnimation",
|
||||
["54"] = "SwimAnimation",
|
||||
["55"] = "WalkAnimation",
|
||||
},
|
||||
|
||||
AnimationAssetTypes = {
|
||||
["48"] = 48,
|
||||
["50"] = 50,
|
||||
["51"] = 51,
|
||||
["52"] = 52,
|
||||
["53"] = 53,
|
||||
["54"] = 54,
|
||||
["55"] = 55,
|
||||
["61"] = 61, -- Emotes
|
||||
},
|
||||
}
|
||||
|
||||
return Constants
|
||||
+1
@@ -0,0 +1 @@
|
||||
return game:DefineFastFlag("FixInspectAndBuyPerformFetch", false)
|
||||
@@ -0,0 +1,10 @@
|
||||
--[[
|
||||
A function to return a fake ID, used for testing
|
||||
]]
|
||||
|
||||
local lastId = 0
|
||||
|
||||
return function()
|
||||
lastId = lastId + 1
|
||||
return ("MOCK-%d"):format(lastId)
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"lint": {
|
||||
"ImplicitReturn": "fatal"
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
--[[
|
||||
Model for an Asset (e.g. Hat).
|
||||
{
|
||||
name = string,
|
||||
assetId = string,
|
||||
assetTypeId = string,
|
||||
creatorId = string,
|
||||
creatorName = string,
|
||||
owned = bool,
|
||||
isForSale = bool,
|
||||
description = string,
|
||||
price = string,
|
||||
productId = string,
|
||||
isLimited = bool,
|
||||
bundlesAssetIsIn = table,
|
||||
numFavorites = int,
|
||||
}
|
||||
]]
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local MockId = require(script.Parent.Parent.MockId)
|
||||
|
||||
local GetFFlagLuaPremiumCatalogIGIAB
|
||||
= require(CoreGui.RobloxGui.Modules.Flags.GetFFlagLuaPremiumCatalogIGIAB)
|
||||
|
||||
local AssetInfo = {}
|
||||
|
||||
function AssetInfo.new()
|
||||
local self = {}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AssetInfo.mock()
|
||||
local self = AssetInfo.new()
|
||||
|
||||
self.name = ""
|
||||
self.assetId = MockId()
|
||||
self.assetTypeId = ""
|
||||
self.creatorId = ""
|
||||
self.creatorName = ""
|
||||
self.owned = false
|
||||
self.isForSale = false
|
||||
self.description = ""
|
||||
self.price = ""
|
||||
self.productId = ""
|
||||
self.isLimited = false
|
||||
self.bundlesAssetIsIn = {}
|
||||
self.numFavorites = 0
|
||||
self.minimumMembershipLevel = 0
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AssetInfo.fromGetProductInfo(assetInfo)
|
||||
local newAsset = AssetInfo.new()
|
||||
|
||||
newAsset.name = assetInfo.Name
|
||||
newAsset.description = assetInfo.Description
|
||||
newAsset.price = assetInfo.PriceInRobux
|
||||
newAsset.creatorId = tostring(assetInfo.Creator.Id)
|
||||
newAsset.creatorName = assetInfo.Creator.Name
|
||||
newAsset.assetId = tostring(assetInfo.AssetId)
|
||||
newAsset.assetTypeId = tostring(assetInfo.AssetTypeId)
|
||||
newAsset.productId = tostring(assetInfo.ProductId)
|
||||
newAsset.isForSale = assetInfo.IsForSale
|
||||
newAsset.isLimited = assetInfo.IsLimited or assetInfo.IsLimitedUnique
|
||||
|
||||
return newAsset
|
||||
end
|
||||
|
||||
function AssetInfo.fromHumanoidDescription(id)
|
||||
local newAsset = AssetInfo.new()
|
||||
|
||||
newAsset.assetId = tostring(id)
|
||||
|
||||
return newAsset
|
||||
end
|
||||
|
||||
function AssetInfo.fromGetAssetBundles(assetId, bundleIds)
|
||||
local newAsset = AssetInfo.new()
|
||||
newAsset.assetId = tostring(assetId)
|
||||
newAsset.bundlesAssetIsIn = bundleIds
|
||||
return newAsset
|
||||
end
|
||||
|
||||
function AssetInfo.fromGetAssetFavoriteCount(assetId, numFavorites)
|
||||
local newAsset = AssetInfo.new()
|
||||
|
||||
newAsset.assetId = tostring(assetId)
|
||||
newAsset.numFavorites = numFavorites
|
||||
|
||||
return newAsset
|
||||
end
|
||||
|
||||
function AssetInfo.fromGetEconomyProductInfo(asset, isOwned, price, isForSale, premiumPricing)
|
||||
local newAsset = AssetInfo.new()
|
||||
|
||||
newAsset.assetId = tostring(asset.assetId)
|
||||
newAsset.owned = isOwned
|
||||
if price then
|
||||
newAsset.price = price
|
||||
end
|
||||
newAsset.isForSale = isForSale
|
||||
if GetFFlagLuaPremiumCatalogIGIAB() then
|
||||
newAsset.premiumPricing = premiumPricing
|
||||
end
|
||||
|
||||
return newAsset
|
||||
end
|
||||
|
||||
function AssetInfo.fromPurchaseSuccess(assetId)
|
||||
local newAsset = AssetInfo.new()
|
||||
newAsset.assetId = tostring(assetId)
|
||||
newAsset.owned = true
|
||||
return newAsset
|
||||
end
|
||||
|
||||
return AssetInfo
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
Model for a Bundle
|
||||
{
|
||||
name = string,
|
||||
bundleId = string,
|
||||
description = string,
|
||||
bundleType = string,
|
||||
creatorId = string,
|
||||
creatorName = string,
|
||||
productId = string,
|
||||
isForSale = bool,
|
||||
price = string,
|
||||
assetIds = table,
|
||||
numFavorites = int,
|
||||
costumeId = string,
|
||||
owned = bool
|
||||
}
|
||||
]]
|
||||
local MockId = require(script.Parent.Parent.MockId)
|
||||
local BundleInfo = {}
|
||||
|
||||
function BundleInfo.new()
|
||||
local self = {}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function BundleInfo.mock()
|
||||
local self = BundleInfo.new()
|
||||
|
||||
self.name = ""
|
||||
self.bundleId = MockId()
|
||||
self.description = ""
|
||||
self.bundleType = ""
|
||||
self.creatorId = ""
|
||||
self.creatorName = ""
|
||||
self.productId = ""
|
||||
self.isForSale = false
|
||||
self.price = ""
|
||||
self.assetIds = {}
|
||||
self.numFavorites = 0
|
||||
self.costumeId = ""
|
||||
self.owned = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function BundleInfo.fromGetAssetBundles(bundleInfo)
|
||||
local newBundle = BundleInfo.new()
|
||||
|
||||
newBundle.name = bundleInfo.name
|
||||
newBundle.bundleId = tostring(bundleInfo.id)
|
||||
newBundle.description = bundleInfo.description
|
||||
newBundle.bundleType = bundleInfo.bundleType
|
||||
newBundle.creatorId = tostring(bundleInfo.creator.id)
|
||||
newBundle.creatorName = bundleInfo.creator.name
|
||||
newBundle.productId = tostring(bundleInfo.product.id)
|
||||
newBundle.assetIds = {}
|
||||
|
||||
for _, asset in pairs(bundleInfo.items) do
|
||||
if asset.type == "UserOutfit" then
|
||||
newBundle.costumeId = tostring(asset.id)
|
||||
else
|
||||
table.insert(newBundle.assetIds, tostring(asset.id))
|
||||
end
|
||||
end
|
||||
|
||||
return newBundle
|
||||
end
|
||||
|
||||
function BundleInfo.fromGetBundleFavoriteCount(bundleId, numFavorites)
|
||||
local newBundle = BundleInfo.new()
|
||||
newBundle.bundleId = tostring(bundleId)
|
||||
newBundle.numFavorites = numFavorites
|
||||
return newBundle
|
||||
end
|
||||
|
||||
function BundleInfo.fromGetEconomyProductInfo(isOwned, bundleId, price, isForSale)
|
||||
local newBundle = BundleInfo.new()
|
||||
newBundle.bundleId = tostring(bundleId)
|
||||
newBundle.owned = isOwned
|
||||
newBundle.price = price
|
||||
newBundle.isForSale = isForSale
|
||||
return newBundle
|
||||
end
|
||||
|
||||
function BundleInfo.fromPurchaseSuccess(bundleId)
|
||||
local newBundle = BundleInfo.new()
|
||||
newBundle.bundleId = tostring(bundleId)
|
||||
newBundle.owned = true
|
||||
return newBundle
|
||||
end
|
||||
|
||||
return BundleInfo
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"lint": {
|
||||
"ImplicitReturn": "fatal"
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Cryo = require(CorePackages.Cryo)
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local AssetInfo = require(InspectAndBuyFolder.Models.AssetInfo)
|
||||
local SetAssets = require(InspectAndBuyFolder.Actions.SetAssets)
|
||||
local SetBundlesAssetIsPartOf = require(InspectAndBuyFolder.Actions.SetBundlesAssetIsPartOf)
|
||||
|
||||
return Rodux.createReducer(
|
||||
{}
|
||||
, {
|
||||
--[[
|
||||
Set a group of assets, joining with any existing assets.
|
||||
]]
|
||||
[SetAssets.name] = function(state, action)
|
||||
local assets = {}
|
||||
|
||||
for _, asset in ipairs(action.assets) do
|
||||
assert(asset.assetId ~= nil, "Expected an asset id when setting an asset's information.")
|
||||
local currentAsset = state[asset.assetId] or {}
|
||||
assets[asset.assetId] = Cryo.Dictionary.join(currentAsset, asset)
|
||||
end
|
||||
|
||||
assets = Cryo.Dictionary.join(state, assets)
|
||||
|
||||
return assets
|
||||
end,
|
||||
|
||||
--[[
|
||||
Sets the list of bundles an asset is part of. At this point
|
||||
the asset should already exist in the store. This is called
|
||||
after the user navigates to the details page.
|
||||
]]
|
||||
[SetBundlesAssetIsPartOf.name] = function(state, action)
|
||||
local assetId = tostring(action.assetId)
|
||||
local bundles = action.bundleIds
|
||||
local currentAsset = state[assetId] or {}
|
||||
local asset = AssetInfo.fromGetAssetBundles(assetId, bundles)
|
||||
asset = Cryo.Dictionary.join(currentAsset, asset)
|
||||
return Cryo.Dictionary.join(state, {[assetId] = asset })
|
||||
end,
|
||||
})
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetAssets = require(InspectAndBuyFolder.Actions.SetAssets)
|
||||
local SetBundlesAssetIsPartOf = require(InspectAndBuyFolder.Actions.SetBundlesAssetIsPartOf)
|
||||
local Assets = require(script.Parent.Assets)
|
||||
local AssetInfo = require(InspectAndBuyFolder.Models.AssetInfo)
|
||||
|
||||
local MOCK_ASSET_1 = AssetInfo.mock()
|
||||
MOCK_ASSET_1.assetId = "123"
|
||||
|
||||
local MOCK_ASSET_2 = AssetInfo.mock()
|
||||
MOCK_ASSET_2.assetId = "456"
|
||||
|
||||
local MOCK_NAME = "Mock Name"
|
||||
|
||||
local function countKeys(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
describe("SetAssets", function()
|
||||
it("should set a single asset", function()
|
||||
local newState = Assets(nil, SetAssets({MOCK_ASSET_1}))
|
||||
|
||||
expect(newState[MOCK_ASSET_1.assetId].assetId).to.equal(MOCK_ASSET_1.assetId)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
|
||||
it("should set a list of assets", function()
|
||||
local newState = Assets(nil, SetAssets({MOCK_ASSET_1, MOCK_ASSET_2}))
|
||||
|
||||
expect(newState[MOCK_ASSET_1.assetId].assetId).to.equal(MOCK_ASSET_1.assetId)
|
||||
expect(newState[MOCK_ASSET_2.assetId].assetId).to.equal(MOCK_ASSET_2.assetId)
|
||||
expect(countKeys(newState)).to.equal(2)
|
||||
end)
|
||||
|
||||
it("should update an asset", function()
|
||||
local newState = Assets(nil, SetAssets({MOCK_ASSET_1}))
|
||||
|
||||
expect(newState[MOCK_ASSET_1.assetId].assetId).to.equal(MOCK_ASSET_1.assetId)
|
||||
|
||||
local updatedAsset = MOCK_ASSET_1
|
||||
updatedAsset.name = MOCK_NAME
|
||||
|
||||
newState = Assets(newState, SetAssets({updatedAsset}))
|
||||
|
||||
expect(newState[MOCK_ASSET_1.assetId].assetId).to.equal(MOCK_ASSET_1.assetId)
|
||||
expect(newState[MOCK_ASSET_1.assetId].name).to.equal(MOCK_NAME)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("SetBundlesAssetIsPartOf", function()
|
||||
it("should set the bundles an asset is part of", function()
|
||||
local newState = Assets(nil, SetAssets({MOCK_ASSET_1}))
|
||||
|
||||
newState = Assets(newState, SetBundlesAssetIsPartOf(
|
||||
MOCK_ASSET_1.assetId,
|
||||
{"1", "2", "3"}
|
||||
))
|
||||
|
||||
expect(newState[MOCK_ASSET_1.assetId].assetId).to.equal(MOCK_ASSET_1.assetId)
|
||||
expect(newState[MOCK_ASSET_1.assetId].bundlesAssetIsIn[1]).to.equal("1")
|
||||
expect(#newState[MOCK_ASSET_1.assetId].bundlesAssetIsIn).to.equal(3)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Cryo = require(CorePackages.Cryo)
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetBundles = require(InspectAndBuyFolder.Actions.SetBundles)
|
||||
|
||||
--[[
|
||||
For V1, in order to prevent going through all pages of the
|
||||
/v1/assets/{assetId}/bundles endpoint for Rthro assets we
|
||||
manually populate Rthro animation bundle data. In the future
|
||||
we only need to get a few bundles at a time as a user scrolls
|
||||
through the list of bundles.
|
||||
]]
|
||||
return Rodux.createReducer(
|
||||
{}
|
||||
, {
|
||||
--[[
|
||||
Set/update bundle information.
|
||||
]]
|
||||
[SetBundles.name] = function(state, action)
|
||||
local bundles = {}
|
||||
|
||||
for _, bundle in ipairs(action.bundles) do
|
||||
assert(bundle.bundleId ~= nil, "Expected a bundle id when setting a bundle's information.")
|
||||
local currentBundle = state[bundle.bundleId] or {}
|
||||
bundles[bundle.bundleId] = Cryo.Dictionary.join(currentBundle, bundle)
|
||||
end
|
||||
|
||||
return Cryo.Dictionary.join(state, bundles)
|
||||
end,
|
||||
})
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetBundles = require(InspectAndBuyFolder.Actions.SetBundles)
|
||||
local Bundles = require(script.Parent.Bundles)
|
||||
local BundleInfo = require(InspectAndBuyFolder.Models.BundleInfo)
|
||||
|
||||
local MOCK_BUNDLE_1 = BundleInfo.mock()
|
||||
MOCK_BUNDLE_1.bundleId = "123"
|
||||
|
||||
local MOCK_BUNDLE_2 = BundleInfo.mock()
|
||||
MOCK_BUNDLE_2.bundleId = "456"
|
||||
|
||||
local function countKeys(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
describe("SetBundles", function()
|
||||
it("should set a bundle's information", function()
|
||||
local newState = Bundles(nil, SetBundles({
|
||||
[1] = MOCK_BUNDLE_1,
|
||||
}))
|
||||
|
||||
expect(newState[MOCK_BUNDLE_1.bundleId].bundleId).to.equal(MOCK_BUNDLE_1.bundleId)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
|
||||
it("should set multiple bundles information", function()
|
||||
local newState = Bundles(nil, SetBundles({
|
||||
[1] = MOCK_BUNDLE_1,
|
||||
[2] = MOCK_BUNDLE_2,
|
||||
}))
|
||||
|
||||
expect(newState[MOCK_BUNDLE_1.bundleId].bundleId).to.equal(MOCK_BUNDLE_1.bundleId)
|
||||
expect(newState[MOCK_BUNDLE_2.bundleId].bundleId).to.equal(MOCK_BUNDLE_2.bundleId)
|
||||
expect(countKeys(newState)).to.equal(2)
|
||||
end)
|
||||
|
||||
it("should update a bundle's information", function()
|
||||
local bundle1 = MOCK_BUNDLE_1
|
||||
local numFavorites = 500
|
||||
local newState = Bundles(nil, SetBundles({
|
||||
[1] = bundle1,
|
||||
}))
|
||||
|
||||
expect(newState[bundle1.bundleId].bundleId).to.equal(bundle1.bundleId)
|
||||
|
||||
bundle1.numFavorites = numFavorites
|
||||
newState = Bundles(newState, SetBundles({
|
||||
[1] = bundle1,
|
||||
}))
|
||||
|
||||
expect(newState[bundle1.bundleId].numFavorites).to.equal(numFavorites)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetDetailsInformation = require(InspectAndBuyFolder.Actions.SetDetailsInformation)
|
||||
|
||||
local DEFAULT_INFORMATION = {
|
||||
viewingDetails = false,
|
||||
assetId = nil,
|
||||
}
|
||||
|
||||
return Rodux.createReducer(
|
||||
DEFAULT_INFORMATION
|
||||
, {
|
||||
[SetDetailsInformation.name] = function(state, action)
|
||||
local newInformation = {
|
||||
viewingDetails = action.viewingDetails,
|
||||
assetId = action.assetId,
|
||||
}
|
||||
|
||||
return newInformation
|
||||
end,
|
||||
})
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetDetailsInformation = require(InspectAndBuyFolder.Actions.SetDetailsInformation)
|
||||
local DetailsInformation = require(script.Parent.DetailsInformation)
|
||||
|
||||
local function countKeys(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
--[[
|
||||
Note: Rthro Animation Bundle is by default in the Bundles reducer.
|
||||
]]
|
||||
describe("SetDetailsInformation", function()
|
||||
it("should set the details information", function()
|
||||
local assetId = "123"
|
||||
local newState = DetailsInformation(nil, SetDetailsInformation(true, assetId))
|
||||
|
||||
expect(newState.viewingDetails).to.equal(true)
|
||||
expect(newState.assetId).to.equal(assetId)
|
||||
expect(countKeys(newState)).to.equal(2)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
-- local Cryo = require(CorePackages.Cryo)
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetEquippedAssets = require(InspectAndBuyFolder.Actions.SetEquippedAssets)
|
||||
|
||||
return Rodux.createReducer(
|
||||
{}
|
||||
, {
|
||||
--[[
|
||||
Set the inspector's equipped assets.
|
||||
]]
|
||||
[SetEquippedAssets.name] = function(state, action)
|
||||
local equippedAssets = {}
|
||||
for _, asset in ipairs(action.equippedAssets) do
|
||||
equippedAssets[tostring(asset.assetId)] = true
|
||||
end
|
||||
return equippedAssets
|
||||
end,
|
||||
})
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetEquippedAssets = require(InspectAndBuyFolder.Actions.SetEquippedAssets)
|
||||
local EquippedAssets = require(script.Parent.EquippedAssets)
|
||||
|
||||
local MOCK_ASSET = {
|
||||
assetId = "123"
|
||||
}
|
||||
local MOCK_ASSET2 = {
|
||||
assetId = "456"
|
||||
}
|
||||
|
||||
local function countKeys(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
describe("SetEquippedAssets", function()
|
||||
it("should set a list of assets as equipped", function()
|
||||
local equippedAssets = {
|
||||
[1] = MOCK_ASSET,
|
||||
[2] = MOCK_ASSET2,
|
||||
}
|
||||
local newState = EquippedAssets(nil, SetEquippedAssets(equippedAssets))
|
||||
expect(newState[MOCK_ASSET.assetId]).to.equal(true)
|
||||
expect(newState[MOCK_ASSET2.assetId]).to.equal(true)
|
||||
expect(newState["TEST"]).to.never.equal(true)
|
||||
expect(countKeys(newState)).to.equal(2)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local Cryo = require(CorePackages.Cryo)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetFavoriteAsset = require(InspectAndBuyFolder.Actions.SetFavoriteAsset)
|
||||
|
||||
return Rodux.createReducer(
|
||||
{}
|
||||
, {
|
||||
[SetFavoriteAsset.name] = function(state, action)
|
||||
local id = action.id
|
||||
local isFavorite = action.isFavorite
|
||||
return Cryo.Dictionary.join(state, {[id] = isFavorite})
|
||||
end
|
||||
})
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetFavoriteAsset = require(InspectAndBuyFolder.Actions.SetFavoriteAsset)
|
||||
local FavoriteAssets = require(script.Parent.FavoriteAssets)
|
||||
|
||||
local MOCK_ID = "123"
|
||||
|
||||
local function countKeys(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
describe("SetFavoriteAsset", function()
|
||||
it("should add a new asset as favorited/unfavorited", function()
|
||||
local newState = FavoriteAssets(nil, SetFavoriteAsset(MOCK_ID, true))
|
||||
expect(newState[MOCK_ID]).to.equal(true)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
|
||||
it("should update the favorite status of an existing asset", function()
|
||||
local newState = FavoriteAssets(nil, SetFavoriteAsset(MOCK_ID, true))
|
||||
expect(newState[MOCK_ID]).to.equal(true)
|
||||
newState = FavoriteAssets(newState, SetFavoriteAsset(MOCK_ID, false))
|
||||
expect(newState[MOCK_ID]).to.equal(false)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local Cryo = require(CorePackages.Cryo)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetFavoriteBundle = require(InspectAndBuyFolder.Actions.SetFavoriteBundle)
|
||||
|
||||
return Rodux.createReducer(
|
||||
{}
|
||||
, {
|
||||
[SetFavoriteBundle.name] = function(state, action)
|
||||
local id = action.id
|
||||
local isFavorite = action.isFavorite
|
||||
return Cryo.Dictionary.join(state, {[id] = isFavorite})
|
||||
end
|
||||
})
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetFavoriteBundle = require(InspectAndBuyFolder.Actions.SetFavoriteBundle)
|
||||
local FavoriteBundles = require(script.Parent.FavoriteBundles)
|
||||
|
||||
local MOCK_ID = "123"
|
||||
|
||||
local function countKeys(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
describe("SetFavoriteBundle", function()
|
||||
it("should add a new bundle as favorited/unfavorited", function()
|
||||
local newState = FavoriteBundles(nil, SetFavoriteBundle(MOCK_ID, true))
|
||||
expect(newState[MOCK_ID]).to.equal(true)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
|
||||
it("should update the favorite status of an existing bundle", function()
|
||||
local newState = FavoriteBundles(nil, SetFavoriteBundle(MOCK_ID, true))
|
||||
expect(newState[MOCK_ID]).to.equal(true)
|
||||
newState = FavoriteBundles(newState, SetFavoriteBundle(MOCK_ID, false))
|
||||
expect(newState[MOCK_ID]).to.equal(false)
|
||||
expect(countKeys(newState)).to.equal(1)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Reducers = script.Parent
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local FavoriteAssets = require(Reducers.FavoriteAssets)
|
||||
local FavoriteBundles = require(Reducers.FavoriteBundles)
|
||||
|
||||
return Rodux.combineReducers({
|
||||
assets = FavoriteAssets,
|
||||
bundles = FavoriteBundles,
|
||||
})
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
return function()
|
||||
local Reducer = require(script.Parent.Favorites)
|
||||
|
||||
it("has the expected fields, and only the expected fields", function()
|
||||
local state = Reducer(nil, {})
|
||||
|
||||
local expectedKeys = {
|
||||
assets = true,
|
||||
bundles = true,
|
||||
}
|
||||
|
||||
for key in pairs(expectedKeys) do
|
||||
assert(state[key] ~= nil, string.format("Expected field %q", key))
|
||||
end
|
||||
|
||||
for key in pairs(state) do
|
||||
assert(expectedKeys[key] ~= nil, string.format("Did not expect field %q", key))
|
||||
end
|
||||
end)
|
||||
end
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
|
||||
local SetGamepadEnabled = require(InspectAndBuyFolder.Actions.SetGamepadEnabled)
|
||||
|
||||
local GamepadEnabled = Rodux.createReducer(false, {
|
||||
[SetGamepadEnabled.name] = function(state, action)
|
||||
return action.gamepadEnabled
|
||||
end,
|
||||
})
|
||||
|
||||
return GamepadEnabled
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local FetchingStatus = require(CorePackages.AppTempCommon.LuaApp.Reducers.FetchingStatus)
|
||||
local Reducers = script.Parent
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local View = require(Reducers.View)
|
||||
local PlayerId = require(Reducers.PlayerId)
|
||||
local PlayerName = require(Reducers.PlayerName)
|
||||
local Assets = require(Reducers.Assets)
|
||||
local Bundles = require(Reducers.Bundles)
|
||||
local EquippedAssets = require(Reducers.EquippedAssets)
|
||||
local DetailsInformation = require(Reducers.DetailsInformation)
|
||||
local TryingOnInfo = require(Reducers.TryingOnInfo)
|
||||
local Favorites = require(Reducers.Favorites)
|
||||
local Locale = require(Reducers.Locale)
|
||||
local Visible = require(Reducers.Visible)
|
||||
local ItemBeingPurchased = require(Reducers.ItemBeingPurchased)
|
||||
local IsLoaded = require(Reducers.IsLoaded)
|
||||
local GamepadEnabled = require(Reducers.GamepadEnabled)
|
||||
local StoreId = require(Reducers.StoreId)
|
||||
|
||||
return Rodux.combineReducers({
|
||||
view = View,
|
||||
playerId = PlayerId,
|
||||
playerName = PlayerName,
|
||||
assets = Assets,
|
||||
bundles = Bundles,
|
||||
equippedAssets = EquippedAssets,
|
||||
detailsInformation = DetailsInformation,
|
||||
tryingOnInfo = TryingOnInfo,
|
||||
favorites = Favorites,
|
||||
locale = Locale,
|
||||
visible = Visible,
|
||||
itemBeingPurchased = ItemBeingPurchased,
|
||||
gamepadEnabled = GamepadEnabled,
|
||||
isLoaded = IsLoaded,
|
||||
FetchingStatus = FetchingStatus,
|
||||
storeId = StoreId,
|
||||
})
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
return function()
|
||||
local Reducer = require(script.Parent.InspectAndBuyReducer)
|
||||
|
||||
it("has the expected fields, and only the expected fields", function()
|
||||
local state = Reducer(nil, {})
|
||||
|
||||
local expectedKeys = {
|
||||
view = true,
|
||||
playerId = true,
|
||||
playerName = true,
|
||||
assets = true,
|
||||
bundles = true,
|
||||
equippedAssets = true,
|
||||
detailsInformation = true,
|
||||
tryingOnInfo = true,
|
||||
favorites = true,
|
||||
locale = true,
|
||||
visible = true,
|
||||
itemBeingPurchased = true,
|
||||
gamepadEnabled = true,
|
||||
isLoaded = true,
|
||||
FetchingStatus = true,
|
||||
storeId = true,
|
||||
}
|
||||
|
||||
for key in pairs(expectedKeys) do
|
||||
assert(state[key] ~= nil, string.format("Expected field %q", key))
|
||||
end
|
||||
|
||||
for key in pairs(state) do
|
||||
assert(expectedKeys[key] ~= nil, string.format("Did not expect field %q", key))
|
||||
end
|
||||
end)
|
||||
end
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
|
||||
local SetAssets = require(InspectAndBuyFolder.Actions.SetAssets)
|
||||
|
||||
local IsLoaded = Rodux.createReducer(false, {
|
||||
[SetAssets.name] = function(state, action)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
return IsLoaded
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
return function()
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetAssets = require(InspectAndBuyFolder.Actions.SetAssets)
|
||||
local IsLoaded = require(script.Parent.IsLoaded)
|
||||
|
||||
describe("IsLoaded", function()
|
||||
it("should mark the Inspect Menu as loaded or not", function()
|
||||
local store = Rodux.Store.new(IsLoaded)
|
||||
local newState = store:getState()
|
||||
expect(newState).to.equal(false)
|
||||
|
||||
newState = IsLoaded(newState, SetAssets({}))
|
||||
expect(newState).to.equal(true)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetItemBeingPurchased = require(InspectAndBuyFolder.Actions.SetItemBeingPurchased)
|
||||
|
||||
local DEFAULT_INFORMATION = {
|
||||
itemType = nil,
|
||||
itemId = nil,
|
||||
}
|
||||
|
||||
return Rodux.createReducer(
|
||||
DEFAULT_INFORMATION
|
||||
, {
|
||||
[SetItemBeingPurchased.name] = function(state, action)
|
||||
local newInformation = {
|
||||
itemType = action.itemType,
|
||||
itemId = action.itemId,
|
||||
}
|
||||
|
||||
return newInformation
|
||||
end,
|
||||
})
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetLocale = require(InspectAndBuyFolder.Actions.SetLocale)
|
||||
|
||||
local FALL_BACK_LOCALE = "en-us"
|
||||
|
||||
return Rodux.createReducer(
|
||||
FALL_BACK_LOCALE
|
||||
, {
|
||||
[SetLocale.name] = function(state, action)
|
||||
return action.locale
|
||||
end,
|
||||
})
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetLocale = require(InspectAndBuyFolder.Actions.SetLocale)
|
||||
local Locale = require(script.Parent.Locale)
|
||||
|
||||
local MOCK_LOCALE = "en-uk"
|
||||
|
||||
describe("SetLocale", function()
|
||||
it("should set the locale", function()
|
||||
local newState = Locale(nil, SetLocale(MOCK_LOCALE))
|
||||
expect(newState).to.equal(MOCK_LOCALE)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetPlayerId = require(InspectAndBuyFolder.Actions.SetPlayerId)
|
||||
|
||||
return Rodux.createReducer(
|
||||
""
|
||||
, {
|
||||
[SetPlayerId.name] = function(state, action)
|
||||
return action.playerId
|
||||
end,
|
||||
})
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetPlayerName = require(InspectAndBuyFolder.Actions.SetPlayerName)
|
||||
|
||||
return Rodux.createReducer(
|
||||
""
|
||||
, {
|
||||
[SetPlayerName.name] = function(state, action)
|
||||
return action.playerName
|
||||
end,
|
||||
})
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local UpdateStoreId = require(InspectAndBuyFolder.Actions.UpdateStoreId)
|
||||
|
||||
local storeIdCounter = 0
|
||||
|
||||
return Rodux.createReducer(
|
||||
"0"
|
||||
, {
|
||||
[UpdateStoreId.name] = function(state, action)
|
||||
storeIdCounter = storeIdCounter + 1
|
||||
return tostring(storeIdCounter)
|
||||
end,
|
||||
})
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetTryingOnInfo = require(InspectAndBuyFolder.Actions.SetTryingOnInfo)
|
||||
|
||||
local DEFAULT_INFO = {
|
||||
tryingOn = false,
|
||||
assetId = nil,
|
||||
assetTypeId = nil,
|
||||
}
|
||||
|
||||
return Rodux.createReducer(
|
||||
DEFAULT_INFO
|
||||
, {
|
||||
[SetTryingOnInfo.name] = function(state, action)
|
||||
local newInfo = {
|
||||
tryingOn = action.tryingOn,
|
||||
assetId = action.assetId,
|
||||
assetTypeId = action.assetTypeId,
|
||||
}
|
||||
|
||||
return newInfo
|
||||
end,
|
||||
})
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
return function()
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetTryingOnInfo = require(InspectAndBuyFolder.Actions.SetTryingOnInfo)
|
||||
local TryingOnInfo = require(script.Parent.TryingOnInfo)
|
||||
|
||||
local function countKeys(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
--[[
|
||||
Note: Rthro Animation Bundle is by default in the Bundles reducer.
|
||||
]]
|
||||
describe("SetTryingOnInfo", function()
|
||||
it("should set the trying on information", function()
|
||||
local assetId = "123"
|
||||
local assetTypeId = "8"
|
||||
local newState = TryingOnInfo(nil, SetTryingOnInfo(true, assetId, assetTypeId))
|
||||
|
||||
expect(newState.tryingOn).to.equal(true)
|
||||
expect(newState.assetId).to.equal(assetId)
|
||||
expect(newState.assetTypeId).to.equal(assetTypeId)
|
||||
expect(countKeys(newState)).to.equal(3)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local SetView = require(InspectAndBuyFolder.Actions.SetView)
|
||||
local Constants = require(InspectAndBuyFolder.Constants)
|
||||
|
||||
return Rodux.createReducer(
|
||||
Constants.View.Wide
|
||||
, {
|
||||
[SetView.name] = function(state, action)
|
||||
return action.view
|
||||
end,
|
||||
})
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
local CorePackages = game:GetService("CorePackages")
|
||||
local Rodux = require(CorePackages.Rodux)
|
||||
local InspectAndBuyFolder = script.Parent.Parent
|
||||
local ShowMenu = require(InspectAndBuyFolder.Actions.ShowMenu)
|
||||
local HideMenu = require(InspectAndBuyFolder.Actions.HideMenu)
|
||||
|
||||
return Rodux.createReducer(
|
||||
true
|
||||
, {
|
||||
[ShowMenu.name] = function(state, action)
|
||||
return true
|
||||
end,
|
||||
[HideMenu.name] = function(state, action)
|
||||
return false
|
||||
end,
|
||||
})
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"lint": {
|
||||
"ImplicitReturn": "fatal"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user