add gs
This commit is contained in:
+136
@@ -0,0 +1,136 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local Modules = CoreGui.RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
|
||||
local Constants = require(Modules.LuaChat.Constants)
|
||||
local formatInteger = require(Modules.LuaChat.Utils.formatInteger)
|
||||
|
||||
local LocalizedTextLabel = require(Modules.LuaApp.Components.LocalizedTextLabel)
|
||||
|
||||
local TEXT_FONT = Enum.Font.SourceSans
|
||||
|
||||
local GAME_NAME_LABEL_HEIGHT = 20
|
||||
local GAME_NAME_LABEL_TEXT_SIZE = 23
|
||||
local GAME_NAME_LABEL_TEXT_COLOR = Constants.Color.GRAY1
|
||||
|
||||
local CREATOR_NAME_LABEL_HEIGHT = 14
|
||||
local CREATOR_NAME_LABEL_TEXT_SIZE = 15
|
||||
local CREATOR_NAME_LABEL_TOP_PADDING = 6
|
||||
local CREATOR_NAME_LABEL_COLOR = Constants.Color.GRAY2
|
||||
|
||||
local SUBTITLE_LABEL_HEIGHT = 14
|
||||
local SUBTITLE_LABEL_TEXT_SIZE = 14
|
||||
local SUBTITLE_LABEL_TOP_PADDING = 3
|
||||
local SUBTITLE_FRAME_HEIGHT = SUBTITLE_LABEL_HEIGHT + SUBTITLE_LABEL_TOP_PADDING
|
||||
|
||||
local ROBUX_ICON = "rbxasset://textures/ui/LuaChat/icons/ic-robux.png"
|
||||
local ROBUX_ICON_SIZE = 12
|
||||
local ROBUX_TO_PRICE_PADDING = 3
|
||||
|
||||
local PRICE_COLOR = Constants.Color.GREEN_PRIMARY
|
||||
|
||||
local FFlagLuaChatShareGameToChatFromChatV2 = settings():GetFFlag("LuaChatShareGameToChatFromChatV2")
|
||||
|
||||
local GameInformation = Roact.PureComponent:extend("GameInformation")
|
||||
|
||||
function GameInformation:render()
|
||||
local gameModel = self.props.gameModel
|
||||
if not gameModel then
|
||||
return nil
|
||||
end
|
||||
|
||||
local gameTitle = gameModel.name
|
||||
local creatorName = gameModel.creatorName
|
||||
-- TODO wait lua app team store the playability of the game, default value is true
|
||||
-- Ticket: MOBLUAPP-683, Review http://swarm.roblox.local/reviews/226723/
|
||||
-- TODO: SOC-3779 This ticket has been committed since this comment.
|
||||
-- * New ticket: https://jira.roblox.com/browse/SOC-3779
|
||||
local playable = FFlagLuaChatShareGameToChatFromChatV2 or self.props.gameModel.isPlayable
|
||||
local gamePrice = gameModel.price
|
||||
local showPrice = gamePrice and playable
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
}),
|
||||
|
||||
Name = Roact.createElement("TextLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Font = TEXT_FONT,
|
||||
LayoutOrder = 1,
|
||||
Size = UDim2.new(1, 0, 0, GAME_NAME_LABEL_HEIGHT),
|
||||
Text = gameTitle,
|
||||
TextColor3 = GAME_NAME_LABEL_TEXT_COLOR,
|
||||
TextSize = GAME_NAME_LABEL_TEXT_SIZE,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Center,
|
||||
}),
|
||||
|
||||
Creator = Roact.createElement(LocalizedTextLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Font = TEXT_FONT,
|
||||
LayoutOrder = 2,
|
||||
Size = UDim2.new(1, 0, 0, CREATOR_NAME_LABEL_HEIGHT + CREATOR_NAME_LABEL_TOP_PADDING),
|
||||
Text = {"Feature.Chat.ShareGameToChat.By", creatorName = creatorName},
|
||||
TextColor3 = CREATOR_NAME_LABEL_COLOR,
|
||||
TextSize = CREATOR_NAME_LABEL_TEXT_SIZE,
|
||||
TextTruncate = Enum.TextTruncate.AtEnd,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Bottom,
|
||||
}),
|
||||
|
||||
NotAvailableTip = not playable and Roact.createElement(LocalizedTextLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
Font = TEXT_FONT,
|
||||
LayoutOrder = 3,
|
||||
Size = UDim2.new(1, 0, 0, SUBTITLE_FRAME_HEIGHT),
|
||||
Text = "Feature.Chat.ShareGameToChat.GameNotAvailable",
|
||||
TextColor3 = CREATOR_NAME_LABEL_COLOR,
|
||||
TextSize = SUBTITLE_LABEL_TEXT_SIZE,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Bottom,
|
||||
}),
|
||||
|
||||
GamePrice = showPrice and Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 3,
|
||||
Size = UDim2.new(1, 0, 0, SUBTITLE_FRAME_HEIGHT),
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Bottom,
|
||||
Padding = UDim.new(0, ROBUX_TO_PRICE_PADDING),
|
||||
}),
|
||||
|
||||
RobuxIcon = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Image = ROBUX_ICON,
|
||||
LayoutOrder = 1,
|
||||
ScaleType = Enum.ScaleType.Fit,
|
||||
Size = UDim2.new(0, ROBUX_ICON_SIZE, 0, ROBUX_ICON_SIZE),
|
||||
}),
|
||||
|
||||
Price = Roact.createElement("TextLabel",{
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, -(ROBUX_ICON_SIZE + ROBUX_TO_PRICE_PADDING), 1, 0),
|
||||
Font = TEXT_FONT,
|
||||
LayoutOrder = 2,
|
||||
Text = formatInteger(gamePrice),
|
||||
TextColor3 = PRICE_COLOR,
|
||||
TextSize = SUBTITLE_LABEL_TEXT_SIZE,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Bottom,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return GameInformation
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
return function()
|
||||
local GameInformation = require(script.Parent.GameInformation)
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
local mockServices = require(Modules.LuaApp.TestHelpers.mockServices)
|
||||
|
||||
local FFlagLuaChatShareGameToChatFromChatV2 = settings():GetFFlag("LuaChatShareGameToChatFromChatV2")
|
||||
|
||||
-- TODO: SOC-3805 `gameModel` should be using an actual model.
|
||||
local function createValidGameModel(isPlayable, price)
|
||||
return {
|
||||
imageToken = "mock-token",
|
||||
name = "mock-name",
|
||||
universeId = "mock-id",
|
||||
isPlayable = isPlayable,
|
||||
price = price or nil,
|
||||
creatorName = "mock-creator",
|
||||
}
|
||||
end
|
||||
|
||||
describe("SHOULD create and destroy without errors", function()
|
||||
it("WHEN passed no props", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation)
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("WHEN passed a valid gameModel", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(),
|
||||
})
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
|
||||
if not FFlagLuaChatShareGameToChatFromChatV2 then
|
||||
describe("SHOULD determine if the game is available", function()
|
||||
describe("SHOULD display playability status visible", function()
|
||||
it("WHEN the game is not playable and free", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(false, nil),
|
||||
})
|
||||
})
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local notAvailableTip = folder:FindFirstChild("NotAvailableTip", true)
|
||||
expect(notAvailableTip).to.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
it("WHEN the game is not playable and not free", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(false, 1000),
|
||||
})
|
||||
})
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local notAvailableTip = folder:FindFirstChild("NotAvailableTip", true)
|
||||
expect(notAvailableTip).to.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
describe("SHOULD display playability status as not visible", function()
|
||||
it("WHEN the game is playable and free", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(true, nil),
|
||||
})
|
||||
})
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local notAvailableTip = folder:FindFirstChild("NotAvailableTip", true)
|
||||
expect(notAvailableTip).to.never.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("SHOULD determine to show price", function()
|
||||
describe("SHOULD display price as visible", function()
|
||||
it("WHEN the game is playable and not free", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(true, 1000),
|
||||
})
|
||||
})
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local gamePrice = folder:FindFirstChild("GamePrice", true)
|
||||
expect(gamePrice).to.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
describe("SHOULD display price as not visible", function()
|
||||
it("WHEN the game is playable and free", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(true, nil),
|
||||
})
|
||||
})
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local gamePrice = folder:FindFirstChild("GamePrice", true)
|
||||
expect(gamePrice).to.never.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
it("WHEN the game is not playable and free", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(false, nil),
|
||||
})
|
||||
})
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local gamePrice = folder:FindFirstChild("GamePrice", true)
|
||||
expect(gamePrice).to.never.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
it("WHEN the game is not playable and not free", function()
|
||||
local element = mockServices({
|
||||
GameInformation = Roact.createElement(GameInformation, {
|
||||
gameModel = createValidGameModel(false, 1000),
|
||||
})
|
||||
})
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local gamePrice = folder:FindFirstChild("GamePrice", true)
|
||||
expect(gamePrice).to.never.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local Modules = CoreGui.RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
|
||||
local GAME_BORDER_ICON = "rbxasset://textures/ui/LuaChat/graphic/gr-game-border-60x60.png"
|
||||
|
||||
local RoundedIcon = Roact.PureComponent:extend("RoundedIcon")
|
||||
|
||||
function RoundedIcon:render()
|
||||
local image = self.props.Image
|
||||
local size = self.props.Size
|
||||
local layoutOrder = self.props.LayoutOrder
|
||||
|
||||
return Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Image = image,
|
||||
Size = size,
|
||||
LayoutOrder = layoutOrder,
|
||||
}, {
|
||||
RoundCornerOverlay = Roact.createElement("ImageLabel", {
|
||||
BackgroundTransparency = 1,
|
||||
Image = GAME_BORDER_ICON,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return RoundedIcon
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
return function()
|
||||
local RoundedIcon = require(script.Parent.RoundedIcon)
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
|
||||
describe("SHOULD create and destroy without errors", function()
|
||||
it("WHEN passed no props", function()
|
||||
local element = Roact.createElement(RoundedIcon)
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
it("WHEN passed an Image", function()
|
||||
local element = Roact.createElement(RoundedIcon, {
|
||||
Image = "mock-id",
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
it("WHEN passed a valid Size", function()
|
||||
local element = Roact.createElement(RoundedIcon, {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local Modules = CoreGui.RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
local isInputTypeTouchOrMouseDown = require(Modules.LuaChat.Utils.isInputTypeTouchOrMouseDown)
|
||||
|
||||
local SEND_BUTTON_ICON = "rbxasset://textures/ui/LuaChat/icons/icon-share-game-24x24.png"
|
||||
local SEND_BUTTON_ICON_PRESSED = "rbxasset://textures/ui/LuaChat/icons/icon-share-game-pressed-24x24.png"
|
||||
|
||||
local SEND_BUTTON_ICON_SIZE = 24
|
||||
local SEND_BUTTON_LEFT_PADDING = 12
|
||||
local SEND_BUTTON_RIGHT_PADDING = 25
|
||||
local SEND_BUTTON_FULL_WIDTH = SEND_BUTTON_ICON_SIZE + SEND_BUTTON_LEFT_PADDING + SEND_BUTTON_RIGHT_PADDING
|
||||
|
||||
|
||||
local SendToChatButton = Roact.PureComponent:extend("SendToChatButton")
|
||||
|
||||
function SendToChatButton.getWidth()
|
||||
return SEND_BUTTON_FULL_WIDTH
|
||||
end
|
||||
|
||||
function SendToChatButton:init()
|
||||
self.state = {
|
||||
isButtonPressed = false,
|
||||
}
|
||||
|
||||
self.onSendButtonInputBegan = function(_, inputObject)
|
||||
if isInputTypeTouchOrMouseDown(inputObject) then
|
||||
self:setState({
|
||||
isButtonPressed = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
self.onSendButtonInputEnded = function(_, inputObject)
|
||||
if isInputTypeTouchOrMouseDown(inputObject) then
|
||||
self:setState({
|
||||
isButtonPressed = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SendToChatButton:render()
|
||||
local onActivated = self.props.onActivated
|
||||
local layoutOrder = self.props.LayoutOrder
|
||||
|
||||
local isButtonPressed = self.state.isButtonPressed
|
||||
|
||||
local sendButtonImage = SEND_BUTTON_ICON
|
||||
if isButtonPressed then
|
||||
sendButtonImage = SEND_BUTTON_ICON_PRESSED
|
||||
end
|
||||
|
||||
return Roact.createElement("ImageButton", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = layoutOrder,
|
||||
Size = UDim2.new(0, SEND_BUTTON_FULL_WIDTH, 1, 0),
|
||||
|
||||
[Roact.Event.InputBegan] = self.onSendButtonInputBegan,
|
||||
[Roact.Event.InputEnded] = self.onSendButtonInputEnded,
|
||||
[Roact.Event.Activated] = onActivated,
|
||||
},{
|
||||
SendButton = Roact.createElement("ImageLabel", {
|
||||
AnchorPoint = Vector2.new(0, 0.5),
|
||||
BackgroundTransparency = 1,
|
||||
Image = sendButtonImage,
|
||||
Position = UDim2.new(0, SEND_BUTTON_LEFT_PADDING, 0.5, 0),
|
||||
Size = UDim2.new(0, SEND_BUTTON_ICON_SIZE, 0, SEND_BUTTON_ICON_SIZE),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return SendToChatButton
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
return function()
|
||||
local SendToChatButton = require(script.Parent.SendToChatButton)
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
|
||||
describe("SHOULD create and destroy without errors", function()
|
||||
it("WHEN passed no props", function()
|
||||
local element = Roact.createElement(SendToChatButton)
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
|
||||
it("SHOULD return a number from getWidth", function()
|
||||
local width = SendToChatButton.getWidth()
|
||||
expect(type(width)).to.equal("number")
|
||||
end)
|
||||
end
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
|
||||
local Modules = CoreGui.RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
local RoactAnalyticsSharedGameItem = require(Modules.LuaChat.Services.RoactAnalyticsSharedGameItem)
|
||||
local RoactRodux = require(Modules.Common.RoactRodux)
|
||||
local RoactServices = require(Modules.LuaApp.RoactServices)
|
||||
|
||||
local GameInformation = require(Modules.LuaChat.Components.ShareGameToChatFromChat.GameInformation)
|
||||
local RoundedIcon = require(Modules.LuaChat.Components.ShareGameToChatFromChat.RoundedIcon)
|
||||
local SendToChatButton = require(Modules.LuaChat.Components.ShareGameToChatFromChat.SendToChatButton)
|
||||
|
||||
local ConversationActions = require(Modules.LuaChat.Actions.ConversationActions)
|
||||
|
||||
local Constants = require(Modules.LuaChat.Constants)
|
||||
local isInputTypeTouchOrMouseDown = require(Modules.LuaChat.Utils.isInputTypeTouchOrMouseDown)
|
||||
|
||||
local UNPRESSED_BACKGROUND_COLOR = Constants.Color.WHITE
|
||||
local PRESSED_BACKGROUND_COLOR = Constants.Color.GRAY5
|
||||
local DEFAULT_SEPARATOR_LINE_COLOR = Constants.Color.GRAY4
|
||||
|
||||
local TOTAL_HEIGHT = 84
|
||||
|
||||
local GAME_ICON_SIZE = Constants.SharedGamesConfig.Thumbnail.SHOWN_SIZE
|
||||
local GAME_ICON_LEFT_PADDING = 15
|
||||
local GAME_ICON_RIGHT_PADDING = 12
|
||||
local GAME_ICON_TOP_PADDING = 12
|
||||
local GAME_ICON_FULL_WIDTH = GAME_ICON_SIZE + GAME_ICON_LEFT_PADDING + GAME_ICON_RIGHT_PADDING
|
||||
|
||||
local GAME_LOADING_ICON = "rbxasset://textures/ui/LuaApp/icons/ic-game.png"
|
||||
|
||||
local SEND_BUTTON_WIDTH = SendToChatButton.getWidth()
|
||||
|
||||
local function verticalSpacer(props)
|
||||
local height = props.height
|
||||
local layoutOrder = props.LayoutOrder
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(1, 0, 0, height),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = layoutOrder,
|
||||
})
|
||||
end
|
||||
|
||||
local function horizontalSpacer(props)
|
||||
local width = props.width
|
||||
local layoutOrder = props.LayoutOrder
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
Size = UDim2.new(0, width, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = layoutOrder,
|
||||
})
|
||||
end
|
||||
|
||||
local SharedGameItem = Roact.PureComponent:extend("SharedGameItem")
|
||||
|
||||
function SharedGameItem:init()
|
||||
self.state = {
|
||||
isGameInputDown = false,
|
||||
}
|
||||
|
||||
self.onGameButtonActivated = function()
|
||||
-- TODO: SOC-3805 Can remove string coercion when using proper model
|
||||
local universeId = self.props.game and tostring(self.props.game.universeId)
|
||||
if universeId then
|
||||
local notificationType = GuiService:GetNotificationTypeList().VIEW_GAME_DETAILS_ANIMATED
|
||||
GuiService:BroadcastNotification(string.format("%s", universeId), notificationType)
|
||||
end
|
||||
end
|
||||
|
||||
self.onGameItemInputBegan = function(_, inputObject)
|
||||
if isInputTypeTouchOrMouseDown(inputObject) then
|
||||
self:setState({
|
||||
isGameInputDown = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
self.onGameItemInputEnded = function(_, inputObject)
|
||||
if isInputTypeTouchOrMouseDown(inputObject) then
|
||||
self:setState({
|
||||
isGameInputDown = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
self.onSendButtonActivated = function()
|
||||
local gameModel = self.props.game
|
||||
|
||||
local activeConversationId = self.props.activeConversationId
|
||||
local analytics = self.props.analytics
|
||||
local gameUrl = self.props.gameUrl
|
||||
local isSharing = self.props.isSharing
|
||||
|
||||
local universeId = gameModel and tostring(gameModel.universeId)
|
||||
|
||||
if universeId then
|
||||
if not isSharing then
|
||||
self.props.shareGameToChat(activeConversationId, analytics, universeId, gameUrl)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SharedGameItem:render()
|
||||
local gameModel = self.props.game
|
||||
local layoutOrder = self.props.layoutOrder
|
||||
|
||||
local gameThumbnails = self.props.gameThumbnails
|
||||
|
||||
-- TODO: SOC-3805 `gameModel` should be using an actual model.
|
||||
-- Currently this is the raw data derived from the WebApi endpoint
|
||||
-- (called from ShareGameToChatFromChatThunks:FetchGames)
|
||||
-- This means we have to coerce all ids into strings manually here
|
||||
local gameId = gameModel and tostring(gameModel.universeId)
|
||||
local gameIcon = gameId and gameThumbnails[gameId] or GAME_LOADING_ICON
|
||||
|
||||
local isGameInputDown = self.state.isGameInputDown
|
||||
local backgroundColor = UNPRESSED_BACKGROUND_COLOR
|
||||
if isGameInputDown then
|
||||
backgroundColor = PRESSED_BACKGROUND_COLOR
|
||||
end
|
||||
|
||||
return Roact.createElement("Frame", {
|
||||
BackgroundColor3 = backgroundColor,
|
||||
BorderSizePixel = 0,
|
||||
LayoutOrder = layoutOrder,
|
||||
Size = UDim2.new(1, 0, 0, TOTAL_HEIGHT),
|
||||
},{
|
||||
Content = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
}),
|
||||
GameButtonContainer = Roact.createElement("ImageButton", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 1,
|
||||
Size = UDim2.new(1, -SEND_BUTTON_WIDTH, 1, 0),
|
||||
|
||||
[Roact.Event.Activated] = self.onGameButtonActivated,
|
||||
[Roact.Event.InputBegan] = self.onGameItemInputBegan,
|
||||
[Roact.Event.InputEnded] = self.onGameItemInputEnded,
|
||||
},{
|
||||
LayoutHorizontal = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
LeftMargin = Roact.createElement(horizontalSpacer, {
|
||||
width = GAME_ICON_LEFT_PADDING,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
GameIconSection = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
LayoutOrder = 2,
|
||||
Size = UDim2.new(0, GAME_ICON_SIZE, 1, 0),
|
||||
}, {
|
||||
LayoutVertical = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
TopMargin = Roact.createElement(verticalSpacer, {
|
||||
height = GAME_ICON_TOP_PADDING,
|
||||
LayoutOrder = 1,
|
||||
}),
|
||||
GameIcon = Roact.createElement(RoundedIcon, {
|
||||
Image = gameIcon,
|
||||
Size = UDim2.new(0, GAME_ICON_SIZE, 0, GAME_ICON_SIZE),
|
||||
LayoutOrder = 2,
|
||||
}),
|
||||
}),
|
||||
IconToInfoPadding = Roact.createElement(horizontalSpacer, {
|
||||
width = GAME_ICON_RIGHT_PADDING,
|
||||
LayoutOrder = 3,
|
||||
}),
|
||||
GameInfoAntiPadding = Roact.createElement("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
Size = UDim2.new(1, -GAME_ICON_FULL_WIDTH, 1, 0),
|
||||
LayoutOrder = 3,
|
||||
}, {
|
||||
GameInfo = gameModel and Roact.createElement(GameInformation, {
|
||||
gameModel = gameModel,
|
||||
}),
|
||||
})
|
||||
}),
|
||||
|
||||
SendButtonContainer = Roact.createElement(SendToChatButton, {
|
||||
onActivated = self.onSendButtonActivated,
|
||||
LayoutOrder = 2,
|
||||
}),
|
||||
}),
|
||||
|
||||
Separator = Roact.createElement("Frame", {
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
BackgroundColor3 = DEFAULT_SEPARATOR_LINE_COLOR,
|
||||
BorderSizePixel = 0,
|
||||
Position = UDim2.new(0, GAME_ICON_FULL_WIDTH, 1, 0),
|
||||
Size = UDim2.new(1, -GAME_ICON_FULL_WIDTH, 0, 1),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
SharedGameItem = RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
-- TODO: SOC-3392 Kill activeConversationId and pass conversationId as routing prop
|
||||
activeConversationId = state.ChatAppReducer.ActiveConversationId,
|
||||
gameThumbnails = state.GameThumbnails,
|
||||
-- TODO: SOC-3896 Allow this to come from parent component and not in store
|
||||
isSharing = state.ChatAppReducer.ShareGameToChatAsync.sharingGame,
|
||||
}
|
||||
end,
|
||||
function(dispatch)
|
||||
return {
|
||||
shareGameToChat = function(activeConversationId, analytics, universeId)
|
||||
analytics.reportShareGameToChatFromChat(activeConversationId, tostring(universeId))
|
||||
return dispatch(ConversationActions.ShareGame(activeConversationId, universeId))
|
||||
end,
|
||||
}
|
||||
end
|
||||
)(SharedGameItem)
|
||||
|
||||
SharedGameItem = RoactServices.connect({
|
||||
analytics = RoactAnalyticsSharedGameItem,
|
||||
})(SharedGameItem)
|
||||
|
||||
return SharedGameItem
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
return function()
|
||||
local SharedGameItem = require(script.Parent.SharedGameItem)
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local AppReducer = require(Modules.LuaApp.AppReducer)
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
local Rodux = require(Modules.Common.Rodux)
|
||||
local mockServices = require(Modules.LuaApp.TestHelpers.mockServices)
|
||||
|
||||
local MOCK_UNIVERSE_ID = "MOCK_UNIVERSE_ID"
|
||||
local MOCK_THUMBNAIL = "MOCK_THUMBNAIL"
|
||||
|
||||
-- TODO: SOC-3805 `gameModel` should be using an actual model.
|
||||
local function createValidGameModel(universeId)
|
||||
return {
|
||||
imageToken = "mock-token",
|
||||
name = "mock-name",
|
||||
universeId = universeId,
|
||||
isPlayable = true,
|
||||
price = 1000,
|
||||
creatorName = "mock-creator",
|
||||
}
|
||||
end
|
||||
|
||||
local storeWithGameThumbnails = Rodux.Store.new(AppReducer, {
|
||||
GameThumbnails = {
|
||||
[MOCK_UNIVERSE_ID] = MOCK_THUMBNAIL,
|
||||
},
|
||||
})
|
||||
|
||||
local function createSharedGameItemTest(universeId)
|
||||
local element = mockServices({
|
||||
SharedGameItem = Roact.createElement(SharedGameItem, {
|
||||
game = createValidGameModel(universeId),
|
||||
}),
|
||||
}, {
|
||||
includeStoreProvider = true,
|
||||
store = storeWithGameThumbnails,
|
||||
})
|
||||
|
||||
return element
|
||||
end
|
||||
|
||||
describe("SHOULD create and destroy without errors", function()
|
||||
it("WHEN passed no props", function()
|
||||
local element = mockServices({
|
||||
SharedGameItem = Roact.createElement(SharedGameItem),
|
||||
}, {
|
||||
includeStoreProvider = true,
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
|
||||
it("WHEN passed a valid gameModel prop", function()
|
||||
local element = createSharedGameItemTest(MOCK_UNIVERSE_ID)
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
local instance = Roact.mount(element, folder)
|
||||
|
||||
local gameInfoInstance = folder:FindFirstChild("GameInfo", true)
|
||||
expect(gameInfoInstance).to.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
local LuaApp = Modules.LuaApp
|
||||
local LuaChat = Modules.LuaChat
|
||||
|
||||
local Immutable = require(Modules.Common.Immutable)
|
||||
local memoize = require(Modules.Common.memoize)
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
local RoactRodux = require(Modules.Common.RoactRodux)
|
||||
local RoactServices = require(Modules.LuaApp.RoactServices)
|
||||
local RoactNetworking = require(Modules.LuaApp.Services.RoactNetworking)
|
||||
|
||||
local LuaAppConstants = require(LuaApp.Constants)
|
||||
local LuaChatConstants = require(LuaChat.Constants)
|
||||
|
||||
local LocalizedTextLabel = require(LuaApp.Components.LocalizedTextLabel)
|
||||
local LoadingBar = require(LuaApp.Components.LoadingBar)
|
||||
local RefreshScrollingFrame = require(LuaApp.Components.RefreshScrollingFrame)
|
||||
local SharedGameItem = require(LuaChat.Components.SharedGameItem)
|
||||
|
||||
local ApiFetchGamesData = require(LuaApp.Thunks.ApiFetchGamesData)
|
||||
local ApiFetchGamesInSort = require(LuaApp.Thunks.ApiFetchGamesInSort)
|
||||
local RetrievalStatus = require(LuaApp.Enum.RetrievalStatus)
|
||||
|
||||
local GAME_ITEM_HEIGHT = 84
|
||||
local NO_GAMES_TIPS_TOP_PADDING = 30
|
||||
local NO_GAMES_TIPS_LABEL_HEIGHT = 25
|
||||
|
||||
local SharedGamesList = Roact.PureComponent:extend("SharedGamesList")
|
||||
|
||||
SharedGamesList.defaultProps = {
|
||||
visible = false
|
||||
}
|
||||
|
||||
function SharedGamesList:init()
|
||||
self.loadMoreGames = function(count)
|
||||
self.isLoadingMore = true
|
||||
local loadCount = count or LuaChatConstants.DEFAULT_GAME_FETCH_COUNT
|
||||
local sorts = self.props.sorts
|
||||
local selectedSortName = self.props.gameSort
|
||||
local dispatchLoadMoreGames = self.props.dispatchLoadMoreGames
|
||||
local networking = self.props.networking
|
||||
|
||||
local selectedSort = sorts[selectedSortName]
|
||||
|
||||
return dispatchLoadMoreGames(networking, selectedSort, selectedSort.rowsRequested, loadCount,
|
||||
selectedSort.nextPageExclusiveStartId)
|
||||
end
|
||||
|
||||
self.refresh = function()
|
||||
return self.props.dispatchRefresh(self.props.networking, self.props.gameSort)
|
||||
end
|
||||
|
||||
if self.props.visible then
|
||||
self.refresh()
|
||||
end
|
||||
end
|
||||
|
||||
function SharedGamesList:willUpdate()
|
||||
if not self.props.fetchingSortTokenStatus or
|
||||
self.props.fetchingSortTokenStatus == RetrievalStatus.Fetching then
|
||||
return
|
||||
end
|
||||
|
||||
if not self.props.fetchingGamesStatus then
|
||||
self.refresh()
|
||||
end
|
||||
end
|
||||
|
||||
function SharedGamesList:render()
|
||||
if not self.props.visible then
|
||||
return nil
|
||||
end
|
||||
|
||||
if not self.props.fetchingSortTokenStatus or
|
||||
self.props.fetchingSortTokenStatus == RetrievalStatus.Fetching then
|
||||
return Roact.createElement(LoadingBar)
|
||||
end
|
||||
|
||||
local games = self.props.games
|
||||
local sortName = self.props.gameSort
|
||||
local sorts = self.props.sorts
|
||||
|
||||
local selectedSort = sorts[sortName]
|
||||
local gameCount = #selectedSort.entries
|
||||
local hasMoreRows = selectedSort.hasMoreRows
|
||||
|
||||
local gamesItems = {}
|
||||
gamesItems["Layout"] = Roact.createElement("UIListLayout", {
|
||||
FillDirection = Enum.FillDirection.Vertical,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
})
|
||||
|
||||
for index = 1, gameCount do
|
||||
local uid = selectedSort.entries[index].universeId
|
||||
gamesItems[index] = Roact.createElement(SharedGameItem, {
|
||||
game = games[uid],
|
||||
itemHeight = GAME_ITEM_HEIGHT,
|
||||
layoutOrder = index,
|
||||
})
|
||||
end
|
||||
|
||||
if (not self.props.fetchingGamesStatus or self.props.fetchingGamesStatus == RetrievalStatus.Fetching)
|
||||
and gameCount == 0 then
|
||||
return Roact.createElement(LoadingBar)
|
||||
end
|
||||
|
||||
return Roact.createElement(RefreshScrollingFrame, {
|
||||
BackgroundColor3 = LuaChatConstants.Color.GRAY6,
|
||||
CanvasSize = UDim2.new(1, 0, 0, 0),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
|
||||
onLoadMore = hasMoreRows and self.loadMoreGames,
|
||||
refresh = self.refresh,
|
||||
}, {
|
||||
SharedGamesList = gameCount > 0 and Roact.createElement("Frame", {
|
||||
BorderSizePixel = 0,
|
||||
Size = UDim2.new(1, 0, 0, gameCount * GAME_ITEM_HEIGHT),
|
||||
LayoutOrder = 3,
|
||||
}, gamesItems),
|
||||
|
||||
NoGamesTip = (gameCount == 0) and Roact.createElement(LocalizedTextLabel, {
|
||||
BackgroundTransparency = 1,
|
||||
BorderSizePixel = 0,
|
||||
Font = Enum.Font.SourceSans,
|
||||
Position = UDim2.new(0, 0, 0, NO_GAMES_TIPS_TOP_PADDING),
|
||||
Size = UDim2.new(1, 0, 0, NO_GAMES_TIPS_LABEL_HEIGHT),
|
||||
Text = LuaChatConstants.SharedGamesConfig.SortsAttribute[sortName].ERROR_TIP_LOCALIZATION_KEY,
|
||||
TextColor3 = LuaChatConstants.Color.GRAY2,
|
||||
TextSize = 20,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
local function sortSorts(a, b)
|
||||
return a.displayName:lower() < b.displayName:lower()
|
||||
end
|
||||
|
||||
local selectSorts = memoize(function(gameSorts, gameSortsContents)
|
||||
local sorts = {}
|
||||
|
||||
for _, sortInfo in pairs(gameSorts) do
|
||||
local gameSortContents = gameSortsContents[sortInfo.name]
|
||||
|
||||
local sort = Immutable.JoinDictionaries(sortInfo, {
|
||||
entries = gameSortContents.entries,
|
||||
rowsRequested = gameSortContents.rowsRequested,
|
||||
hasMoreRows = gameSortContents.hasMoreRows,
|
||||
nextPageExclusiveStartId = gameSortContents.nextPageExclusiveStartId,
|
||||
})
|
||||
|
||||
table.insert(sorts, sort)
|
||||
sorts[sort.name] = sort
|
||||
end
|
||||
|
||||
table.sort(sorts, sortSorts)
|
||||
return sorts
|
||||
end)
|
||||
|
||||
SharedGamesList = RoactRodux.UNSTABLE_connect2(
|
||||
function(state, props)
|
||||
return {
|
||||
fetchingSortTokenStatus = state.RequestsStatus.GameSortTokenFetchingStatus[LuaAppConstants.GameSortGroups.ChatGames],
|
||||
fetchingGamesStatus = state.RequestsStatus.GameSortsStatus[props.gameSort],
|
||||
games = state.Games,
|
||||
sorts = selectSorts(state.GameSorts, state.GameSortsContents),
|
||||
}
|
||||
end,
|
||||
|
||||
function(dispatch)
|
||||
return {
|
||||
dispatchRefresh = function(networking, sortName)
|
||||
return dispatch(ApiFetchGamesData(networking, nil, sortName))
|
||||
end,
|
||||
|
||||
dispatchLoadMoreGames = function(networking, sort, startRows, maxRows, nextPageExclusiveStartId)
|
||||
return dispatch(ApiFetchGamesInSort(networking, sort, true, {
|
||||
startRows = startRows,
|
||||
maxRows = maxRows,
|
||||
exclusiveStartId = nextPageExclusiveStartId
|
||||
}))
|
||||
end
|
||||
}
|
||||
end
|
||||
)(SharedGamesList)
|
||||
|
||||
return RoactServices.connect({
|
||||
networking = RoactNetworking,
|
||||
})(SharedGamesList)
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
return function()
|
||||
local SharedGamesList = require(script.Parent.SharedGamesList)
|
||||
|
||||
local Modules = game:GetService("CoreGui").RobloxGui.Modules
|
||||
|
||||
local Roact = require(Modules.Common.Roact)
|
||||
local mockServices = require(Modules.LuaApp.TestHelpers.mockServices)
|
||||
|
||||
it("should create and destroy without errors", function()
|
||||
local element = mockServices({
|
||||
SharedGamesList = Roact.createElement(SharedGamesList, {
|
||||
gameSort = "Popular",
|
||||
}),
|
||||
}, {
|
||||
includeStoreProvider = true,
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element)
|
||||
Roact.unmount(instance)
|
||||
end)
|
||||
end
|
||||
Reference in New Issue
Block a user