local CorePackages = game:GetService("CorePackages") local Cryo = require(CorePackages.Cryo) local Actions = CorePackages.AppTempCommon.LuaApp.Actions local TableUtilities = require(CorePackages.AppTempCommon.LuaApp.TableUtilities) local PromiseUtilities = require(CorePackages.AppTempCommon.LuaApp.PromiseUtilities) local ThumbnailsGetAvatar = require(CorePackages.AppTempCommon.LuaApp.Http.Requests.ThumbnailsGetAvatar) local ThumbnailsGetAvatarHeadshot = require(CorePackages.AppTempCommon.LuaApp.Http.Requests.ThumbnailsGetAvatarHeadshot) local AvatarThumbnailTypes = require(CorePackages.AppTempCommon.LuaApp.Enum.AvatarThumbnailTypes) local SetUserThumbnail = require(Actions.SetUserThumbnail) local Promise = require(CorePackages.AppTempCommon.LuaApp.Promise) local PerformFetch = require(CorePackages.AppTempCommon.LuaApp.Thunks.Networking.Util.PerformFetch) local Result = require(CorePackages.AppTempCommon.LuaApp.Result) local RETRY_MAX_COUNT = math.max(0, settings():GetFVariable("LuaAppNonFinalThumbnailMaxRetries")) local RETRY_TIME_MULTIPLIER = math.max(0, settings():GetFVariable("LuaAppThumbnailsApiRetryTimeMultiplier")) local MAX_REQUEST_COUNT = 100 local ThumbnailsTypeToApiMap = { [AvatarThumbnailTypes.AvatarThumbnail] = ThumbnailsGetAvatar, [AvatarThumbnailTypes.HeadShot] = ThumbnailsGetAvatarHeadshot, } local function subdivideEntries(entries, limit) local subArrays = {} for i = 1, #entries, limit do local subArray = Cryo.List.getRange(entries, i, i + limit - 1) table.insert(subArrays, subArray) end return subArrays end local function keyMapper(userId, thumbnailType, thumbnailSize) return "luaapp.usersthumbnailsapi." .. userId .. "." .. thumbnailType .. "." .. thumbnailSize end local function isCompleteThumbnailData(entry) return type(entry) == "table" and type(entry.targetId) == "number" and type(entry.state) == "string" and type(entry.imageUrl) == "string" end local ApiFetchUsersThumbnail = {} function ApiFetchUsersThumbnail.getThumbnailsSizeArgForSize(thumbnailSize) assert(typeof(thumbnailSize) == "string", string.format("ApiFetchUsersThumbnail expects a string for thumbnailSize. Type: %s", typeof(thumbnailSize)) ) assert(string.match(thumbnailSize, 'Size.+x'), string.format( "ApiFetchUsersThumbnail expects thumbnailSize to follow format \"Size..x..\" Current thumbnailSize: ", thumbnailSize ) ) return string.gsub(thumbnailSize, "Size", "") end function ApiFetchUsersThumbnail._fetch(networkImpl, listOfUserIds, thumbnailRequest) local thumbnailSize = thumbnailRequest.thumbnailSize local thumbnailType = thumbnailRequest.thumbnailType local thumbnailSizeRequestArg = ApiFetchUsersThumbnail.getThumbnailsSizeArgForSize(thumbnailSize) local thumbnailsApiForThumbnailType = ThumbnailsTypeToApiMap[thumbnailType] assert(typeof(thumbnailType) == "string", "ApiFetchUsersThumbnail expects thumbnailType to be a string") assert(typeof(thumbnailsApiForThumbnailType) == "function", "ApiFetchUsersThumbnail failed to find api for given type: ", thumbnailType) local function keyMapperForCurrentTypeAndSize(userId) return keyMapper(userId, thumbnailType, thumbnailSize) end local function getTableOfFailedResults(userIds) local results = {} for _, userId in pairs(userIds) do local key = keyMapperForCurrentTypeAndSize(userId) results[key] = Result.new(false, { targetId = userId, }) end return results end return PerformFetch.Batch(listOfUserIds, keyMapperForCurrentTypeAndSize, function(store, userIdsToFetch) local function fetchThumbnails(userIdsProvided) return thumbnailsApiForThumbnailType(networkImpl, userIdsProvided, thumbnailSizeRequestArg):andThen( function(result) local results = getTableOfFailedResults(userIdsProvided) local data = result and result.responseBody and result.responseBody.data if typeof(data) == "table" then for _, entry in pairs(data) do if isCompleteThumbnailData(entry) then local userId = tostring(entry.targetId) local key = keyMapperForCurrentTypeAndSize(userId) local success = false if entry.state == "Completed" then store:dispatch(SetUserThumbnail(tostring(entry.targetId), entry.imageUrl, thumbnailType, thumbnailSize)) success = true end results[key] = Result.new(success, entry) end end end return Promise.resolve(results) end, function(err) local results = getTableOfFailedResults(userIdsProvided) return Promise.resolve(results) end ) end return fetchThumbnails(userIdsToFetch):andThen(function(results) local completedThumbnails = {} local thumbnailResults = results if _G.__TESTEZ_RUNNING_TEST__ then RETRY_MAX_COUNT = 1 RETRY_TIME_MULTIPLIER = 0.001 end local function retry(retryCount) local remainingUserIdsToFetch = {} for key, result in pairs(thumbnailResults) do local isSuccessful, thumbnailInfo = result:unwrap() if isSuccessful and thumbnailInfo.state == "Completed" then completedThumbnails[key] = result elseif isSuccessful and thumbnailInfo.state == "Pending" then table.insert(remainingUserIdsToFetch, thumbnailInfo.targetId) end end if TableUtilities.FieldCount(remainingUserIdsToFetch) == 0 then return Promise.resolve(completedThumbnails) end local delayPromise = Promise.new(function(resolve, reject) coroutine.wrap(function() wait(RETRY_TIME_MULTIPLIER * math.pow(2, retryCount - 1)) resolve() end)() end) return delayPromise:andThen(function() return fetchThumbnails(remainingUserIdsToFetch) end):andThen(function(newResults) thumbnailResults = newResults if retryCount > 1 then return retry(retryCount - 1) else return Promise.resolve(completedThumbnails) end end) end return retry(RETRY_MAX_COUNT) end) end) end function ApiFetchUsersThumbnail.Fetch(networkImpl, userIds, thumbnailRequests) return function(store) local allPromises = {} local subArraysOfUserIds = subdivideEntries(userIds, MAX_REQUEST_COUNT) for _, thumbnailRequest in pairs(thumbnailRequests) do for _, limitedListOfUserIds in pairs(subArraysOfUserIds) do local promise = store:dispatch(ApiFetchUsersThumbnail._fetch(networkImpl, limitedListOfUserIds, thumbnailRequest)) table.insert(allPromises, promise) end end return PromiseUtilities.Batch(allPromises) end end function ApiFetchUsersThumbnail.GetFetchingStatus(state, userId, thumbnailType, thumbnailSize) return PerformFetch.GetStatus(state, keyMapper(userId, thumbnailType, thumbnailSize)) end return ApiFetchUsersThumbnail