This commit is contained in:
lx
2026-07-06 14:01:11 -04:00
commit c4f97d729d
16468 changed files with 935321 additions and 0 deletions
@@ -0,0 +1,91 @@
local CorePackages = game:GetService("CorePackages")
local LuaApp = CorePackages.AppTempCommon.LuaApp
local GamesGetThumbnails = require(LuaApp.Http.Requests.GamesGetThumbnails)
local SetGameThumbnails = require(LuaApp.Actions.SetGameThumbnails)
local Functional = require(CorePackages.AppTempCommon.Common.Functional)
local Promise = require(LuaApp.Promise)
local Result = require(LuaApp.Result)
local TableUtilities = require(LuaApp.TableUtilities)
local THUMBNAIL_PAGE_COUNT = 20
local THUMBNAIL_SIZE = 150
local RETRY_MAX_COUNT = math.max(0, settings():GetFVariable("LuaAppNonFinalThumbnailMaxRetries"))
local RETRY_TIME_MULTIPLIER = 2 -- seconds
local function convertToId(value)
if type(value) ~= "number" and type(value) ~= "string" then
return Result.error("convertToId expects value passed in to be a number or a string")
end
return Result.success(tostring(value))
end
local function subdivideThumbnailTokenArray(thumbnailTokens, tokenLimit)
local someTokens = {}
for i = 1, #thumbnailTokens, tokenLimit do
local subArray = Functional.Take(thumbnailTokens, tokenLimit, i)
table.insert(someTokens, subArray)
end
return someTokens
end
local function fetchThumbnailBatch(networkImpl, store, thumbnailTokens)
return GamesGetThumbnails(networkImpl, thumbnailTokens, THUMBNAIL_SIZE, THUMBNAIL_SIZE):andThen(function(result)
local thumbnails = {}
local unfinalizedThumbnails = {}
for _,image in pairs(result.responseBody) do
local convertToIdResult = convertToId(image.universeId)
convertToIdResult:match(function(universeId)
if image.final == false then
unfinalizedThumbnails[universeId] = image.retryToken
else
-- index all of the thumbnails by universeId
thumbnails[universeId] = image
end
end, function(convertToIdError)
warn(convertToIdError)
end)
end
store:dispatch(SetGameThumbnails(thumbnails))
return Promise.resolve(unfinalizedThumbnails)
end)
end
local function fetchSubdividedThumbnailsArray(networkImpl, store, thumbnailTokens)
return fetchThumbnailBatch(networkImpl, store, thumbnailTokens):andThen(function(unfinalizedThumbnails)
local remainingUnfinalizedThumbnails = unfinalizedThumbnails
for retryCount = 1, RETRY_MAX_COUNT do
if TableUtilities.FieldCount(remainingUnfinalizedThumbnails) == 0 then
return -- Bail out, we're done!
end
wait(RETRY_TIME_MULTIPLIER * math.pow(2, retryCount - 1))
remainingUnfinalizedThumbnails = fetchThumbnailBatch(networkImpl, store, remainingUnfinalizedThumbnails):await()
end
end)
end
local function fetchThumbnails(networkImpl, thumbnailTokens)
return function(store)
-- NOTE : because the size of each thumbnail token, me must limit the number we can fetch at a time.
-- So break apart the array of tokens we get into smaller, more manageable pieces.
local fetchPromises = {}
local someTokens = subdivideThumbnailTokenArray(thumbnailTokens, THUMBNAIL_PAGE_COUNT)
for _, thumbsArr in ipairs(someTokens) do
local promise = fetchSubdividedThumbnailsArray(networkImpl, store, thumbsArr)
table.insert(fetchPromises, promise)
end
return Promise.all(fetchPromises)
end
end
return fetchThumbnails
@@ -0,0 +1,40 @@
local CorePackages = game:GetService("CorePackages")
local ApiFetchGameThumbnails = require(CorePackages.AppTempCommon.LuaApp.Thunks.ApiFetchGameThumbnails)
local Functional = require(CorePackages.AppTempCommon.Common.Functional)
local GamesMultigetPlaceDetails = require(CorePackages.AppTempCommon.LuaApp.Http.Requests.GamesMultigetPlaceDetails)
local PlaceInfoModel = require(CorePackages.AppTempCommon.LuaChat.Models.PlaceInfoModel)
local ReceivedPlacesInfos = require(CorePackages.AppTempCommon.LuaApp.Actions.ReceivedPlacesInfos)
return function(networkImpl, placeIds)
return function(store)
if not placeIds or #placeIds == 0 then
return
end
return GamesMultigetPlaceDetails(networkImpl, placeIds):andThen(function(result)
local data = result.responseBody
local imageTokens = {}
local placeInfos = Functional.Map(data, function(placeInfoData)
local placeInfo = PlaceInfoModel.fromWeb(placeInfoData)
local universePlaceInfos = store:getState().UniversePlaceInfos
local universeId = placeInfo.universeRootPlaceId
if not universePlaceInfos[universeId] or placeInfo.imageToken ~= universePlaceInfos[universeId].imageToken then
table.insert(imageTokens, placeInfo.imageToken)
end
return placeInfo
end)
store:dispatch(ReceivedPlacesInfos(placeInfos))
if #imageTokens > 0 then
store:dispatch(ApiFetchGameThumbnails(networkImpl, imageTokens))
end
return placeInfos
end)
end
end
@@ -0,0 +1,24 @@
local CorePackages = game:GetService("CorePackages")
local Functional = require(CorePackages.AppTempCommon.Common.Functional)
local GetPlaceInfos = require(CorePackages.AppTempCommon.LuaApp.Http.Requests.GetPlaceInfos)
-- LuaChat
local PlaceInfoModel = require(CorePackages.AppTempCommon.LuaChat.Models.PlaceInfoModel)
local ReceivedMultiplePlaceInfos = require(CorePackages.AppTempCommon.LuaChat.Actions.ReceivedMultiplePlaceInfos)
return function(networkImpl, placeIds)
return function(store)
return GetPlaceInfos(networkImpl, placeIds):andThen(function(result)
local data = result.responseBody
local placeInfos = Functional.Map(data, function(placeInfoData)
return PlaceInfoModel.fromWeb(placeInfoData)
end)
store:dispatch(ReceivedMultiplePlaceInfos(placeInfos))
return placeInfos
end)
end
end
@@ -0,0 +1,21 @@
local CorePackages = game:GetService("CorePackages")
local Actions = CorePackages.AppTempCommon.LuaApp.Actions
local Requests = CorePackages.AppTempCommon.LuaApp.Http.Requests
local UsersGetFriendCount = require(Requests.UsersGetFriendCount)
local SetFriendCount = require(Actions.SetFriendCount)
return function(networkImpl)
return function(store)
return UsersGetFriendCount(networkImpl):andThen(function(result)
local data = result.responseBody
if data.success and data.count then
store:dispatch(SetFriendCount(data.count))
end
return data.count
end)
end
end
@@ -0,0 +1,63 @@
local CorePackages = game:GetService("CorePackages")
local Requests = CorePackages.AppTempCommon.LuaApp.Http.Requests
local Promise = require(CorePackages.AppTempCommon.LuaApp.Promise)
local ApiFetchUsersPresences = require(CorePackages.AppTempCommon.LuaApp.Thunks.ApiFetchUsersPresences)
local ApiFetchUsersThumbnail = require(CorePackages.AppTempCommon.LuaApp.Thunks.ApiFetchUsersThumbnail)
local ApiFetchUsersFriendCount = require(CorePackages.AppTempCommon.LuaApp.Thunks.ApiFetchUsersFriendCount)
local UsersGetFriends = require(Requests.UsersGetFriends)
local AddUsers = require(CorePackages.AppTempCommon.LuaApp.Actions.AddUsers)
local FetchUserFriendsStarted = require(CorePackages.AppTempCommon.LuaApp.Actions.FetchUserFriendsStarted)
local FetchUserFriendsFailed = require(CorePackages.AppTempCommon.LuaApp.Actions.FetchUserFriendsFailed)
local FetchUserFriendsCompleted = require(CorePackages.AppTempCommon.LuaApp.Actions.FetchUserFriendsCompleted)
local UserModel = require(CorePackages.AppTempCommon.LuaApp.Models.User)
local UpdateUsers = require(CorePackages.AppTempCommon.LuaApp.Thunks.UpdateUsers)
local LuaAppRemoveGetFriendshipCountApiCalls = settings():GetFFlag("LuaAppRemoveGetFriendshipCountApiCalls")
local homePageDataFetchRefactor = settings():GetFFlag('LuaHomePageDataFetchRefactor')
return function(requestImpl, userId, thumbnailRequest)
return function(store)
store:dispatch(FetchUserFriendsStarted(userId))
if not LuaAppRemoveGetFriendshipCountApiCalls then
store:dispatch(ApiFetchUsersFriendCount(requestImpl))
end
return UsersGetFriends(requestImpl, userId):andThen(function(response)
local responseBody = response.responseBody
local userIds = {}
local newUsers = {}
for _, userData in pairs(responseBody.data) do
local id = tostring(userData.id)
local newUser = UserModel.fromData(id, userData.name, true)
table.insert(userIds, id)
newUsers[newUser.id] = newUser
end
if LuaAppRemoveGetFriendshipCountApiCalls then
store:dispatch(UpdateUsers(newUsers))
else
store:dispatch(AddUsers(newUsers))
end
return userIds
end):andThen(function(userIds)
-- Asynchronously fetch friend thumbnails so we don't block display of UI
store:dispatch(ApiFetchUsersThumbnail(requestImpl, userIds, thumbnailRequest))
return store:dispatch(ApiFetchUsersPresences(requestImpl, userIds))
end):andThen(
function(result)
store:dispatch(FetchUserFriendsCompleted(userId))
if homePageDataFetchRefactor then
return Promise.resolve(result)
end
end,
function(response)
store:dispatch(FetchUserFriendsFailed(userId, response))
if homePageDataFetchRefactor then
return Promise.reject(response)
end
end
)
end
end
@@ -0,0 +1,26 @@
local CorePackages = game:GetService("CorePackages")
local Utils = CorePackages.AppTempCommon.LuaChat.Utils
local getPlaceIds = require(Utils.getFriendsActiveGamesPlaceIdsFromUsersPresence)
local receiveUsersPresence = require(Utils.receiveUsersPresence)
local ApiFetchGamesDataByPlaceIds = require(CorePackages.AppTempCommon.LuaApp.Thunks.ApiFetchGamesDataByPlaceIds)
local UsersGetPresence = require(CorePackages.AppTempCommon.LuaApp.Http.Requests.UsersGetPresence)
local FFlagLuaHomeGetFriendsPlayingGamesInfo = settings():GetFFlag("LuaHomeGetFriendsPlayingGamesInfo")
return function(networkImpl, userIds)
return function(store)
return UsersGetPresence(networkImpl, userIds):andThen(function(result)
local userPresences = result.responseBody.userPresences
receiveUsersPresence(userPresences, store)
if FFlagLuaHomeGetFriendsPlayingGamesInfo then
local placeIds = getPlaceIds(userPresences, store)
store:dispatch(ApiFetchGamesDataByPlaceIds(networkImpl, placeIds))
end
end)
end
end
@@ -0,0 +1,40 @@
local CorePackages = game:GetService("CorePackages")
local Actions = CorePackages.AppTempCommon.LuaApp.Actions
local Requests = CorePackages.AppTempCommon.LuaApp.Http.Requests
local UsersGetThumbnail = require(Requests.UsersGetThumbnail)
local SetUserThumbnail = require(Actions.SetUserThumbnail)
local Promise = require(CorePackages.AppTempCommon.LuaApp.Promise)
local function fetchThumbnailsBatch(networkImpl, userIds, thumbnailRequest)
local fetchedPromises = {}
for _, userId in pairs(userIds) do
table.insert(fetchedPromises,
UsersGetThumbnail(userId, thumbnailRequest.thumbnailType, thumbnailRequest.thumbnailSize)
)
end
return Promise.all(fetchedPromises)
end
return function(networkImpl, userIds, thumbnailRequests)
return function(store)
-- We currently cannot batch request user avatar thumbnails,
-- so each thumbnailRequest has to be processed individually.
local fetchedPromises = {}
for _, thumbnailRequest in pairs(thumbnailRequests) do
table.insert(fetchedPromises,
fetchThumbnailsBatch(networkImpl, userIds, thumbnailRequest):andThen(function(result)
for _, data in pairs(result) do
store:dispatch(SetUserThumbnail(data.id, data.image, data.thumbnailType, data.thumbnailSize))
end
end)
)
end
return Promise.all(fetchedPromises)
end
end
@@ -0,0 +1,42 @@
local CorePackages = game:GetService("CorePackages")
local Players = game:GetService("Players")
local AppTempCommon = CorePackages.AppTempCommon
local Requests = CorePackages.AppTempCommon.LuaApp.Http.Requests
local ChatSendMessage = require(Requests.ChatSendMessage)
local ChatStartOneToOneConversation = require(Requests.ChatStartOneToOneConversation)
local Url = require(CorePackages.AppTempCommon.LuaApp.Http.Url)
local trimCharacterFromEndString = require(AppTempCommon.Temp.trimCharacterFromEndString)
local INVITE_TEXT_MESSAGE = "Come join me in %s"
local INVITE_LINK_MESSAGE = "%s/games/%s"
return function(networkImpl, userId, placeInfo)
local clientId = Players.LocalPlayer.UserId
-- Construct the invite messages based on place info
local inviteTextMessage = string.format(INVITE_TEXT_MESSAGE, placeInfo.name)
local trimmedUrl = trimCharacterFromEndString(Url.WWW_URL, "/")
local inviteLinkMessage = string.format(INVITE_LINK_MESSAGE, trimmedUrl, placeInfo.universeRootPlaceId)
return function(store)
return ChatStartOneToOneConversation(networkImpl, userId, clientId):andThen(function(conversationResult)
local conversation = conversationResult.responseBody.conversation
return ChatSendMessage(networkImpl, conversation.id, inviteTextMessage):andThen(function()
return ChatSendMessage(networkImpl, conversation.id, inviteLinkMessage):andThen(function(inviteResult)
local data = inviteResult.responseBody
return {
resultType = data.resultType,
conversationId = conversation.id,
placeId = placeInfo.universeRootPlaceId,
}
end)
end)
end)
end
end
@@ -0,0 +1,50 @@
local CorePackages = game:GetService("CorePackages")
local User = require(CorePackages.AppTempCommon.LuaApp.Models.User)
local AddUsers = require(CorePackages.AppTempCommon.LuaApp.Actions.AddUsers)
local SetFriendCount = require(CorePackages.AppTempCommon.LuaApp.Actions.SetFriendCount)
return function(users)
return function(store)
local friendCountOffset = 0
local updatedUsers = {}
for _, user in pairs(users) do
local needsUpdate = false
local userId = user.id
local isFriend = user.isFriend
local offset = 0
assert(typeof(isFriend) == "boolean")
local userInStore = store:getState().Users[userId]
if userInStore then
-- Mark user with needsUpdate if any of the field is different
-- from the existing user information in Store.
if not User.compare(userInStore, user) then
needsUpdate = true
if userInStore.isFriend ~= isFriend then
offset = isFriend and 1 or -1
end
end
else
needsUpdate = true
offset = isFriend and 1 or 0
end
if needsUpdate then
friendCountOffset = friendCountOffset + offset
updatedUsers[userId] = user
end
end
if next(updatedUsers) then
store:dispatch(AddUsers(updatedUsers))
end
if friendCountOffset ~= 0 then
local currentFriendCount = store:getState().FriendCount
store:dispatch(SetFriendCount(currentFriendCount + friendCountOffset))
end
end
end
@@ -0,0 +1,123 @@
return function()
local CorePackages = game:GetService("CorePackages")
local Rodux = require(CorePackages.Rodux)
local Immutable = require(CorePackages.AppTempCommon.Common.Immutable)
local UpdateUsers = require(CorePackages.AppTempCommon.LuaApp.Thunks.UpdateUsers)
local AddUsers = require(CorePackages.AppTempCommon.LuaApp.Actions.AddUsers)
local SetFriendCount = require(CorePackages.AppTempCommon.LuaApp.Actions.SetFriendCount)
local FriendCount = require(CorePackages.AppTempCommon.LuaChat.Reducers.FriendCount)
local Users = require(CorePackages.AppTempCommon.LuaApp.Reducers.Users)
local User = require(CorePackages.AppTempCommon.LuaApp.Models.User)
local function UsersReducerMonitor (state, action)
state = state or {
numberOfAddUsersCalled = 0,
numberOfUsersPassedIn = 0,
}
if action.type == AddUsers.name then
state.numberOfAddUsersCalled = state.numberOfAddUsersCalled + 1
state.numberOfUsersPassedIn = 0
for _, _ in pairs(action.users) do
state.numberOfUsersPassedIn = state.numberOfUsersPassedIn + 1
end
end
return state
end
local function FriendCountReducerMonitor (state, action)
state = state or {
numberOfSetFriendCountCalled = 0,
}
if action.type == SetFriendCount.name then
state.numberOfSetFriendCountCalled = state.numberOfSetFriendCountCalled + 1
end
return state
end
local function CustomReducer(state, action)
state = state or {}
return {
Users = Users(state.Users, action),
UsersReducerMonitor = UsersReducerMonitor(state.UsersReducerMonitor, action),
FriendCount = FriendCount(state.FriendCount, action),
FriendCountReducerMonitor = FriendCountReducerMonitor(state.FriendCountReducerMonitor, action),
}
end
local listOfUsers = {
["1"] = User.fromData(1, "Hedonism Bot", true),
["2"] = User.fromData(2, "Hypno Toad", true),
["3"] = User.fromData(3, "John Zoidberg", false),
["4"] = User.fromData(4, "Pazuzu", true),
["5"] = User.fromData(5, "Ogden Wernstrom", true),
["6"] = User.fromData(6, "Lrrr", true),
}
it("should do nothing if empty list of users is provided", function()
local store = Rodux.Store.new(CustomReducer, {}, {
Rodux.thunkMiddleware,
})
store:dispatch(UpdateUsers({ }))
local state = store:getState()
expect(state.UsersReducerMonitor.numberOfAddUsersCalled).to.equal(0)
expect(state.FriendCountReducerMonitor.numberOfSetFriendCountCalled).to.equal(0)
end)
it("should update only the number of users with modified data", function()
local store = Rodux.Store.new(CustomReducer, {
Users = listOfUsers,
}, {
Rodux.thunkMiddleware,
})
local currentUsers = store:getState().Users
local listOfUsersWithPotentialUpdates = {
Immutable.Set(currentUsers["2"], "presence", User.PresenceType.IN_GAME), -- changed
Immutable.Set(currentUsers["5"], "isFriend", false), -- changed
Immutable.Set(currentUsers["6"], "isFriend", true), -- did not change
}
store:dispatch(UpdateUsers(listOfUsersWithPotentialUpdates))
local state = store:getState()
expect(state.UsersReducerMonitor.numberOfAddUsersCalled).to.equal(1)
expect(state.UsersReducerMonitor.numberOfUsersPassedIn).to.equal(2)
end)
it("should correctly update the number of friends", function()
local store = Rodux.Store.new(CustomReducer, {}, {
Rodux.thunkMiddleware,
})
store:dispatch(UpdateUsers(listOfUsers))
local state = store:getState()
expect(state.FriendCountReducerMonitor.numberOfSetFriendCountCalled).to.equal(1)
expect(state.FriendCount).to.equal(5)
local currentUsers = store:getState().Users
local listOfUsersWithPotentialUpdates = {
Immutable.Set(currentUsers["2"], "presence", User.PresenceType.IN_GAME), -- friendship didn't change
Immutable.Set(currentUsers["5"], "isFriend", false), -- friendship changed
Immutable.Set(currentUsers["6"], "isFriend", false), -- friendship changed
User.fromData(7, "Nibbler", true), -- new friend
}
store:dispatch(UpdateUsers(listOfUsersWithPotentialUpdates))
state = store:getState()
expect(state.FriendCount).to.equal(4)
end)
end