Clients/Client2018/content/internal/AppShell/Modules/Shell/GamesView.lua

524 lines
14 KiB
Lua

local CoreGui = game:GetService("CoreGui")
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")
local ShellModules = Modules:FindFirstChild("Shell")
local GuiService = game:GetService('GuiService')
local Strings = require(ShellModules:FindFirstChild('LocalizedStrings'))
local Utility = require(ShellModules:FindFirstChild('Utility'))
local SortsData = require(ShellModules:FindFirstChild('SortsData'))
local FFlagXboxUseCuratedGameSort = SortsData.FFlagXboxNewGameSortsEndpoint
local ScrollingGrid = require(ShellModules:FindFirstChild('ScrollingGrid'))
local GameCarouselItem = require(ShellModules:FindFirstChild('GameCarouselItem'))
local GlobalSettings = require(ShellModules:FindFirstChild('GlobalSettings'))
local EventHub = require(ShellModules:FindFirstChild('EventHub'))
local LockedGameCarouselView = require(ShellModules:FindFirstChild('LockedGameCarouselView'))
local AchievementManager = require(ShellModules:FindFirstChild('AchievementManager'))
local createGamesView = function(viewGridConfig, onNewGameSelected)
local viewGridContainer = ScrollingGrid(viewGridConfig)
viewGridContainer.Container.Visible = false
local this = {}
local carouselItems = {}
--The local savedCarouselItems which will always hold all the carouselItems even
--there is no games in it
local savedCarouselItems = {}
local selectedCoreObjectChangedCn = nil
local setLoadDebounce = false
local inFocus = false
local lastLoadTime = nil
local sortCategories = nil
local savedSelectedCarouselItem = nil
local staticSelectionImage = Utility.Create'ImageLabel'
{
-- Values pulled from GuiService.cpp; see constructor
-- these numbers will need to be updated if positions/sizes change
Name = "StaticSelectionImage";
Size = UDim2.new(0, 232 + 14, 0, 232 + 14);
BackgroundTransparency = 1;
ScaleType = Enum.ScaleType.Slice;
SliceCenter = Rect.new(19, 19, 43, 43);
Image = "rbxasset://textures/ui/SelectionBox.png";
Selectable = false;
ZIndex = 3;
}
local lockView = LockedGameCarouselView()
lockView:SetSelectionImageObject(
Utility.Create'ImageLabel'
{
BackgroundTransparency = 1;
}
)
lockView:SetParent(nil)
lockView:SetZIndex(2)
local function GetSelectionRectangleFromFirstItem(firstItemContainer)
return UDim2.new(
firstItemContainer.Position.X.Scale, firstItemContainer.Position.X.Offset - 7,
firstItemContainer.Position.Y.Scale, firstItemContainer.Position.Y.Offset + 50 - 7)
end
local function showStaticSelectionImage()
local firstSortItem = #carouselItems > 0 and carouselItems[1] or nil
if firstSortItem then
local firstItemContainer = firstSortItem:GetContainer()
local pos = GetSelectionRectangleFromFirstItem(firstItemContainer)
staticSelectionImage.Position = pos
staticSelectionImage.Size = UDim2.new(0, 232 + 14, 0, 232 + 14);
staticSelectionImage.Parent = viewGridContainer.Container.Parent
end
lockView:SetParent(nil)
end
local function enterNormalSelectionMode()
lockView:SetParent(nil)
local firstSortItem = #carouselItems > 0 and carouselItems[1] or nil
if firstSortItem then
local firstItemContainer = firstSortItem:GetContainer()
local pos = GetSelectionRectangleFromFirstItem(firstItemContainer)
staticSelectionImage.Position = pos
staticSelectionImage.Size = UDim2.new(0, 232 + 14, 0, 232 + 14);
end
end
local function enterLockedRowSelectionMode()
local firstSortItem = #carouselItems > 0 and carouselItems[1] or nil
if firstSortItem then
local firstItemView = firstSortItem:GetCarouselView()
if firstItemView then
local viewContainerSize = firstItemView:GetContainer().Size
-- scaling comes from GameCarouselItem
local lockViewSize = UDim2.new(0, viewContainerSize.X.Offset, 0, viewContainerSize.Y.Offset * 0.84)
lockView:SetSize(lockViewSize)
lockView:SetPosition(UDim2.new(0, 8, 0, 8))
staticSelectionImage.Size = lockViewSize + UDim2.new(0, 16, 0, 16)
local viewContainerPos = firstItemView:GetContainer().Position
staticSelectionImage.Position = UDim2.new(
viewContainerPos.X.Scale,
viewContainerPos.X.Offset - 118,
viewContainerPos.Y.Scale,
viewContainerPos.Y.Offset + 10
)
end
end
lockView:SetParent(staticSelectionImage)
end
local function removeStaticSelectionImage()
staticSelectionImage.Parent = nil
end
local fadableObjets = {}
local function getItemByIndex(i)
local item = carouselItems[i]
return item and item:GetContainer()
end
local function getIndexByItem(item)
for i = 1, #carouselItems do
if item == carouselItems[i] then
return i
end
end
return 0
end
local function getMappedIndex(item)
for i = 1, #savedCarouselItems do
if item == savedCarouselItems[i] then
return i
end
end
return 0
end
local lastSelectedRow = 0
local function GetSelectedRow()
for i = 1, #carouselItems do
local item = carouselItems[i]
if item:IsSelected() then
return i
end
end
return 0
end
local function GetSelectedItem()
for i = 1, #carouselItems do
local item = carouselItems[i]
if item:IsSelected() then
return item
end
end
return nil
end
local function onSelectedCoreObjectChanged()
if setLoadDebounce then return end
local currentSelectedRow = GetSelectedRow()
lastSelectedRow = currentSelectedRow
local selectedItem = nil
if currentSelectedRow == 0 then
for i = 1, #carouselItems do
local item = carouselItems[i]
item:RemoveFocus()
item:SetTransparency(0, 0, 0.5)
end
else
for i = 1, #carouselItems do
local item = carouselItems[i]
local imageTransparency = 0
local textTransparency = 0
if item:IsSelected() then
item:Focus()
showStaticSelectionImage()
selectedItem = item
else
if i < currentSelectedRow then
-- Making these bigger than one causes the fadeout to happen before the carousel
-- overlaps with the game info and preview-image above. It looks better.
imageTransparency = 4
textTransparency = 4
elseif i == currentSelectedRow then
imageTransparency = 0
textTransparency = 0
else
imageTransparency = 0.5
textTransparency = 0.5
end
item:RemoveFocus()
end
item:SetTransparency(imageTransparency, textTransparency, nil)
end
if selectedItem then
if selectedItem:IsLocked() then
enterLockedRowSelectionMode()
else
enterNormalSelectionMode()
end
end
end
end
local function UpdateTransparency()
local currentSelectedRow = GetSelectedRow()
if currentSelectedRow > 0 then
for i = 1, #carouselItems do
local item = carouselItems[i]
local imageTransparency = 0
local textTransparency = 0
if i < currentSelectedRow then
imageTransparency = 4
textTransparency = 4
elseif i == currentSelectedRow then
imageTransparency = 0
textTransparency = 0
else
imageTransparency = 0.5
textTransparency = 0.5
end
item:SetTransparency(imageTransparency, textTransparency, 0, true)
end
end
end
local function carouselItemsResultsUpdate(item)
if not item then return end
local lastItemIndex = getIndexByItem(item)
if item:HasResults() then
--Add a new row
if lastItemIndex == 0 then
local prevSelectedRow = GetSelectedRow()
local Inserted = false
for i = 1, #carouselItems do
if getMappedIndex(item) < getMappedIndex(carouselItems[i]) then
table.insert(carouselItems, i, item)
Inserted = true
break
end
end
if not Inserted then
table.insert(carouselItems, item)
end
--Recalc when carouselItems changes
viewGridContainer:RecalcLayout(#carouselItems + 1)
--Try to select the same row after insertion happened
carouselItems[prevSelectedRow]:Focus()
end
else
--Remove a row
if lastItemIndex > 0 then
local wasSelected = item:IsSelected()
item:RemoveFocus()
--If was in the carouselItems, remove
table.remove(carouselItems, lastItemIndex)
viewGridContainer:RecalcLayout(#carouselItems + 1)
--The selected row get removed, select the same selected row or first row if doesn't exist
if inFocus and wasSelected then
if lastItemIndex > 0 and lastItemIndex <= #carouselItems then
carouselItems[lastItemIndex]:Focus()
else
viewGridContainer:BackToInitial()
if #carouselItems > 0 then
carouselItems[1]:Focus()
end
end
end
lastSelectedRow = GetSelectedRow()
end
end
UpdateTransparency()
end
local function createCarouselItem(name, getGameCollection)
local item = GameCarouselItem(UDim2.new(0, 1900, 0, 250), name, getGameCollection, onNewGameSelected, carouselItemsResultsUpdate)
table.insert(carouselItems, item)
table.insert(savedCarouselItems, item)
return item
end
local function createCarouselItems(sortCategories)
local featuredSortId = 3
for i = 1, #sortCategories do
local sort = sortCategories[i]
local item = createCarouselItem(sort["Name"], function() return sort end)
if not sort["userId"] then --If the sort is not related to user
if not FFlagXboxUseCuratedGameSort then
if item and sort["Id"] ~= featuredSortId then
item:SetLockInPUP(true)
end
else
local sortGameSetTargetId = sort["GameSetTargetId"] or 0
if item and not (sort["Id"] == featuredSortId and sortGameSetTargetId == 0) then
item:SetLockInPUP(true)
end
end
end
end
viewGridContainer:SetItemCallback(getItemByIndex)
viewGridContainer:RecalcLayout(#carouselItems + 1)
for i = 1, #savedCarouselItems do
if not AchievementManager:AllGamesUnlocked() and savedCarouselItems[i]:GetLockInPUP() then
savedCarouselItems[i]:Lock()
else
savedCarouselItems[i]:Unlock()
end
end
for i = 1, #savedCarouselItems do
savedCarouselItems[i]:Init()
end
end
--Load function for SortsData
local sortsDataVersion = nil
local function LoadGameSorts()
if setLoadDebounce then return end
setLoadDebounce = true
local needUpdate = false
local sortsData = nil
if SortsData:HasSorts() then --Sync Call
sortsData = SortsData:GetSorts()
if not sortsDataVersion or sortsData.Version ~= sortsDataVersion then
needUpdate = true
end
else
needUpdate = true
end
if needUpdate then
viewGridContainer.Container.Visible = false
spawn(function()
local maxRetry = 2
while maxRetry > 0 and not sortsData do
sortsData = SortsData:GetSorts()
maxRetry = maxRetry - 1
end
if sortsData then
if #savedCarouselItems > 0 then
--Remove all
for i = 1, #savedCarouselItems do
savedCarouselItems[i]:Destroy()
end
end
--Reset all data
carouselItems = {}
savedCarouselItems = {}
savedSelectedCarouselItem = nil
onNewGameSelected(nil)
viewGridContainer:RecalcLayout(0)
viewGridContainer:BackToInitial()
lastSelectedRow = 0
--Create new CarouselItems
createCarouselItems(sortsData.Data)
sortsDataVersion = sortsData.Version
end
viewGridContainer.Container.Visible = true
if inFocus then
if #carouselItems > 0 then
carouselItems[1]:Focus()
showStaticSelectionImage()
UpdateTransparency()
end
end
setLoadDebounce = false
end)
else
setLoadDebounce = false
end
end
local function onGridViewFocused()
--Focus on viewGridContainer and the selected row
viewGridContainer:Focus()
if not setLoadDebounce then
local selectedRow = 0
if savedSelectedCarouselItem then
selectedRow = getIndexByItem(savedSelectedCarouselItem)
savedSelectedCarouselItem = nil
else
selectedRow = GetSelectedRow()
end
if selectedRow > 0 and selectedRow <= #carouselItems then
carouselItems[selectedRow]:Focus()
else
viewGridContainer:BackToInitial()
if #carouselItems > 0 then
carouselItems[1]:Focus()
end
end
UpdateTransparency()
end
end
local function onGridViewFocusRemoved(fromAppHub)
savedSelectedCarouselItem = GetSelectedItem()
lastSelectedRow = 0
--If the bumper/button B caused the removing focus, reset to the top of viewGridContainer
if fromAppHub then
viewGridContainer:BackToInitial(GlobalSettings.TabDockTweenDuration)
savedSelectedCarouselItem = nil
end
--Remove image and the focus on all inside elements
removeStaticSelectionImage()
viewGridContainer:RemoveFocus()
for i = 1, #carouselItems do
local item = carouselItems[i]
item:RemoveFocus()
item:SetTransparency(0, 0, 0.5)
end
end
function this:GetDefaultFocusItem()
return nil
end
function this:SetParent(parent)
viewGridContainer:SetParent(parent)
end
function this:Load()
LoadGameSorts()
end
function this:Focus()
inFocus = true
Utility.DisconnectEvent(selectedCoreObjectChangedCn)
selectedCoreObjectChangedCn = GuiService:GetPropertyChangedSignal('SelectedCoreObject'):connect(function()
onSelectedCoreObjectChanged()
end)
onGridViewFocused()
end
function this:RemoveFocus(fromAppHub)
inFocus = false
Utility.DisconnectEvent(selectedCoreObjectChangedCn)
onGridViewFocusRemoved(fromAppHub)
end
EventHub:removeEventListener(EventHub.Notifications["PlayedGamesChanged"], "GamesPaneRecentlyPlayedUpdate")
EventHub:removeEventListener(EventHub.Notifications["FavoriteToggle"], "GamesPaneFavoritesUpdate")
EventHub:removeEventListener(EventHub.Notifications["UnlockedUGC"], "GamesPaneUnlockedUGC")
--Add listener to PlayedGamesChanged and FavoriteToggle to reload these two types timely
EventHub:addEventListener(EventHub.Notifications["PlayedGamesChanged"], "GamesPaneRecentlyPlayedUpdate",
function()
spawn(function()
local updateItemName = Strings:LocalizedString("RecentlyPlayedSortTitle")
for i = 1, #savedCarouselItems do
if updateItemName == savedCarouselItems[i]:GetSortName() then
savedCarouselItems[i]:Refresh()
break
end
end
end)
end)
EventHub:addEventListener(EventHub.Notifications["FavoriteToggle"], "GamesPaneFavoritesUpdate",
function(success)
if success then
spawn(function()
local updateItemName = Strings:LocalizedString("FavoritesSortTitle")
for i = 1, #savedCarouselItems do
if updateItemName == savedCarouselItems[i]:GetSortName() then
savedCarouselItems[i]:Refresh()
break
end
end
end)
end
end)
if not AchievementManager:AllGamesUnlocked() then
EventHub:addEventListener(EventHub.Notifications["UnlockedUGC"], "GamesPaneUnlockedUGC",
function()
spawn(function()
for i = 1, #savedCarouselItems do
if savedCarouselItems[i]:IsLocked() then
savedCarouselItems[i]:Unlock()
end
end
end)
end)
end
--[[ Initialize - Don't Block ]]--
spawn(function()
LoadGameSorts()
end)
return this
end
return createGamesView