diff --git a/compcount.lua b/compcount.lua index 911460d..d107a3d 100644 --- a/compcount.lua +++ b/compcount.lua @@ -53,6 +53,8 @@ local fallbackIcon = false local backButton = false local scrollUpButton = false local scrollDownButton = false +local overviewCachedCounts = {} +local overviewCachedSortedItemIds = nil local overviewScrollCurrentRow = 0 local overviewScrollTargetRow = 0 local drawOverview @@ -330,6 +332,42 @@ local function getMeItemCounts(itemIds) return counts end +local function makeZeroCounts(itemIds) + local counts = {} + + for i = 1, #itemIds do + counts[itemIds[i]] = 0 + end + + return counts +end + +local function createAsyncMeItemCountsJob(itemIds, batchSize) + local counts = makeZeroCounts(itemIds) + local index = 1 + + batchSize = math.max(1, math.floor(tonumber(batchSize) or 1)) + + return { + step = function() + local bridge = ensureStorageBridge() + + if not bridge then + return true, counts + end + + local lastIndex = math.min(#itemIds, index + batchSize - 1) + + for i = index, lastIndex do + counts[itemIds[i]] = getMeItemCount(itemIds[i]) + end + + index = lastIndex + 1 + return index > #itemIds, counts + end, + } +end + local function getFallbackIcon() if fallbackIcon == false then fallbackIcon = parseNfpImage(table.concat({ @@ -566,8 +604,8 @@ local function drawItem(img, cellX, cellY, count, offsetY) end local function runScrollableGrid(options) - local entries = {} - local counts = {} + local entries = options.getInitialEntries() + local counts = options.getInitialCounts(entries) local visibleItemCount = 0 local totalRows = 0 local maxScrollRow = 0 @@ -575,6 +613,9 @@ local function runScrollableGrid(options) local targetScrollRow = 0 local refreshTimer local animationTimer + local refreshJob + local refreshJobTimer + local queuedRefresh = false local columns = 3 local rowsPerView = 3 @@ -645,8 +686,19 @@ local function runScrollableGrid(options) end end - local function refreshGridData() - entries, counts = options.getEntriesAndCounts() + local function startRefreshJob() + if refreshJob then + return false + end + + refreshJob = options.createRefreshJob(entries, counts) + + if not refreshJob then + return false + end + + refreshJobTimer = os.startTimer(options.refreshStepSeconds or 0.05) + return true end local function renderGrid() @@ -704,18 +756,41 @@ local function runScrollableGrid(options) frame:drawBuffer() end - refreshGridData() renderGrid() + startRefreshJob() refreshTimer = os.startTimer(options.refreshSeconds or 30) while true do local event, p1, x, y = os.pullEvent() if event == "timer" and p1 == refreshTimer then - refreshGridData() - renderGrid() + if refreshJob then + queuedRefresh = true + else + startRefreshJob() + end refreshTimer = os.startTimer(options.refreshSeconds or 30) scheduleAnimation() + elseif event == "timer" and p1 == refreshJobTimer then + local isDone, newEntries, newCounts = refreshJob.step() + + if isDone then + refreshJob = nil + refreshJobTimer = nil + + if newEntries and newCounts then + entries = newEntries + counts = newCounts + renderGrid() + end + + if queuedRefresh then + queuedRefresh = false + startRefreshJob() + end + else + refreshJobTimer = os.startTimer(options.refreshStepSeconds or 0.05) + end elseif event == "timer" and p1 == animationTimer then local delta = targetScrollRow - currentScrollRow @@ -799,8 +874,26 @@ local function drawPage(base_id) runScrollableGrid({ refreshSeconds = 5, - getEntriesAndCounts = function() - return pageItems, getMeItemCounts(pageItemIds) + getInitialEntries = function() + return pageItems + end, + getInitialCounts = function() + return makeZeroCounts(pageItemIds) + end, + createRefreshJob = function(currentEntries) + local job = createAsyncMeItemCountsJob(pageItemIds, 3) + + return { + step = function() + local isDone, itemCounts = job.step() + + if isDone then + return true, currentEntries, itemCounts + end + + return false + end, + } end, getId = function(entry) return entry.id @@ -829,8 +922,39 @@ drawOverview = function() local items = getBaseItemIds() local defaultIcon = getFallbackIcon() + local function sortOverviewItems(itemCounts) + local sortedItemIds = {} + + for i = 1, #items do + sortedItemIds[i] = items[i] + end + + table.sort(sortedItemIds, function(a, b) + local countA = itemCounts[a] or 0 + local countB = itemCounts[b] or 0 + + if countA ~= countB then + return countA > countB + end + + return a < b + end) + + return sortedItemIds + end + runScrollableGrid({ refreshSeconds = 30, + getInitialEntries = function() + if overviewCachedSortedItemIds and #overviewCachedSortedItemIds > 0 then + return overviewCachedSortedItemIds + end + + return sortOverviewItems(overviewCachedCounts) + end, + getInitialCounts = function() + return overviewCachedCounts + end, getInitialScrollState = function() return overviewScrollCurrentRow, overviewScrollTargetRow end, @@ -838,26 +962,22 @@ drawOverview = function() overviewScrollCurrentRow = currentRow overviewScrollTargetRow = targetRow end, - getEntriesAndCounts = function() - local itemCounts = getMeItemCounts(items) - local sortedItemIds = {} + createRefreshJob = function() + local job = createAsyncMeItemCountsJob(items, 6) - for i = 1, #items do - sortedItemIds[i] = items[i] - end + return { + step = function() + local isDone, itemCounts = job.step() - table.sort(sortedItemIds, function(a, b) - local countA = itemCounts[a] or 0 - local countB = itemCounts[b] or 0 + if isDone then + overviewCachedCounts = itemCounts + overviewCachedSortedItemIds = sortOverviewItems(overviewCachedCounts) + return true, overviewCachedSortedItemIds, overviewCachedCounts + end - if countA ~= countB then - return countA > countB - end - - return a < b - end) - - return sortedItemIds, itemCounts + return false + end, + } end, getId = function(entry) return entry