Merge pull request #786 from ShaneMcGovernIE/find-mods-release-stats

Show feed-published release stats on Find Mods rows
This commit is contained in:
bryanthaboi
2026-08-04 08:00:46 -04:00
committed by GitHub
6 changed files with 85 additions and 7 deletions
+7
View File
@@ -471,6 +471,13 @@ automatically, so the tab opens on an "Add an index" prompt until you name
one; paste an index URL or its `owner/repo` and it is remembered in
`options.lua`. More than one index can be added, and the listings merge.
A feed author can publish per-mod release stats by adding three optional
fields to an entry -- `downloads` (total across every release), and
`first_release` / `last_release` (ISO days) -- which the listing shows in
the same gold line the MODS tab uses. The fields are additive: feeds that
carry them stay readable by every build that predates them, and feeds that
do not render exactly as before.
## Soft reset (all versions)
Holding A, B, START and SELECT together restarts the game the way flicking
+14 -7
View File
@@ -1241,13 +1241,9 @@ local function buildModsPanel(imp, parent, m)
-- counts, so a pre-downloads cache entry costs the line, not a wrong "0".
local dlLine
if info and info.downloads then
local formatted = ModUpdate.formatCount(info.downloads.total)
if info.dates then
dlLine = Strings("%s downloads across all releases - Released %s - Updated %s",
formatted, info.dates.first, info.dates.latest)
else
dlLine = Strings("%s downloads across all releases", formatted)
end
local d = info.dates
dlLine = ModUpdate.statsLine(info.downloads.total,
d and d.first, d and d.latest)
end
-- measure the body: name (with the badge beside it only when it fits),
@@ -1522,9 +1518,17 @@ local function buildFindPanel(imp, parent, m)
local btnH = math.ceil(textHeight(chipSize)) + 14
for _, entry in ipairs(rows) do
local action, note = findActionFor(entry, installed[entry.id])
-- Feed-published release stats (downloads, first/last release date) in
-- the same gold line the MODS tab uses; absent until a feed carries them.
local statsLine
if entry.downloads ~= nil or entry.first_release or entry.last_release then
statsLine = ModUpdate.statsLine(entry.downloads,
entry.first_release, entry.last_release)
end
local bodyH = math.ceil(textHeight(titleSize))
+ 4 + math.ceil(textHeight(smallSize))
if statsLine then bodyH = bodyH + 4 + wrapHeight(smallSize, statsLine, bodyW) end
if note then bodyH = bodyH + 4 + wrapHeight(smallSize, note, bodyW) end
if entry.summary and entry.summary ~= "" then
bodyH = bodyH + 4 + wrapHeight(smallSize, entry.summary, bodyW)
@@ -1569,6 +1573,9 @@ local function buildFindPanel(imp, parent, m)
end
label(body, meta, smallSize, C("detail"),
{ width = "100%", textWrap = false, textOverflow = "ellipsis" })
if statsLine then
label(body, statsLine, smallSize, C("gold"), { width = "100%" })
end
if note then label(body, note, smallSize, C("green"), { width = "100%" }) end
if entry.summary and entry.summary ~= "" then
label(body, entry.summary, smallSize, C("detail"), { width = "100%" })
+7
View File
@@ -174,6 +174,13 @@ local function parseEntry(raw)
conflicts = raw.conflicts,
thumbnail = str(raw.thumbnail),
description_url = str(raw.description_url),
-- Optional release stats a feed author can publish: total downloads
-- across all releases plus first/last release dates. Additive-only,
-- so a feed that carries them stays readable by every build that
-- predates them (and one that does not still renders fine here).
downloads = tonumber(raw.downloads),
first_release = str(raw.first_release),
last_release = str(raw.last_release),
latest = parseLatest(raw.latest),
update_check = str(raw.update_check) or "pending",
}
+20
View File
@@ -7,6 +7,8 @@ local ModUpdate = {}
ModUpdate.CACHE_TTL = 6 * 60 * 60 -- six hours
local Strings = require("src.core.Strings")
local function stripV(tag)
return (tostring(tag):gsub("^[vV]", ""))
end
@@ -237,6 +239,24 @@ function ModUpdate.formatCount(n)
return (s:gsub("^,", ""))
end
-- The one-line stats string both launcher panels show: "1,234 downloads
-- across all releases - Released 2024-05-31 - Updated 2026-07-01".
-- Each part is optional; nil everywhere means nil, so a row with no data
-- shows no line rather than a wrong "0".
function ModUpdate.statsLine(total, first, latest)
local parts = {}
if total ~= nil then
parts[#parts + 1] = Strings("%s downloads across all releases",
ModUpdate.formatCount(total))
end
if first or latest then
parts[#parts + 1] = Strings("Released %s - Updated %s",
first or "?", latest or "?")
end
if #parts == 0 then return nil end
return table.concat(parts, " - ")
end
-- ------- cache (options.modUpdateCache[repo])
local function cacheStore()
+21
View File
@@ -110,6 +110,27 @@ do
"the release asset URL survives parsing")
eq(m.permissions[1], "engine_internals", "permissions are kept")
eq(m.update_check, "ok", "update_check is kept")
check(m.downloads == nil and m.first_release == nil and m.last_release == nil,
"a feed without release stats parses them as absent")
end
-- release stats a feed can publish: total downloads and first/last dates
-- ride along additively, so a feed carrying them stays readable by every
-- build that predates them
do
local withStats = {}
for k, v in pairs(NUZLOCKE) do withStats[k] = v end
withStats.downloads = 1234
withStats.first_release = "2024-05-31"
withStats.last_release = "2026-07-01"
local index = ModIndex.parse(feed({ withStats }))
local m = index.mods[1]
eq(m.downloads, 1234, "total downloads are kept")
eq(m.first_release, "2024-05-31", "first release date is kept")
eq(m.last_release, "2026-07-01", "last release date is kept")
withStats.downloads = "9999"
m = ModIndex.parse(feed({ withStats })).mods[1]
eq(m.downloads, 9999, "numeric-string downloads are coerced")
end
-- schema_version is a contract, not a hint: an unknown one is refused rather
+16
View File
@@ -183,6 +183,22 @@ eq(ModUpdate.formatCount("12345"), "12,345", "numeric strings are accepted")
eq(ModUpdate.formatCount(nil), "0", "nil formats as zero")
eq(ModUpdate.formatCount("garbage"), "0", "garbage formats as zero")
-- statsLine: the shared launcher row line, part by part
eq(ModUpdate.statsLine(1234567, "2024-05-31", "2026-07-01"),
"1,234,567 downloads across all releases - Released 2024-05-31 - Updated 2026-07-01",
"full stats line")
eq(ModUpdate.statsLine(82, nil, nil),
"82 downloads across all releases",
"downloads alone")
eq(ModUpdate.statsLine(nil, "2024-05-31", "2026-07-01"),
"Released 2024-05-31 - Updated 2026-07-01",
"dates alone")
eq(ModUpdate.statsLine(0, nil, nil),
"0 downloads across all releases",
"a real zero still shows")
check(ModUpdate.statsLine(nil, nil, nil) == nil,
"no data at all means no line")
-- cacheUsable: a cache entry from before downloads existed must not be
-- trusted, everything current is
do