Add a Statistics tab with a card for every soundtrack
Build / test (push) Successful in 40s
Build / build (push) Successful in 1m16s

Part of #41. The top bar has Quiz and Statistics tabs instead of the
Statistics button. The Statistics tab replaces the quiz below the top bar
(the song keeps playing) and shows every soundtrack as a card with its
plays, last played date and most and least played songs, loaded from the
new GET /api/v1/statistics/soundtracks. The cards can be searched (with
the Search popup's matching, now in SoundtrackSearch), filtered on played
or never played, and sorted A-Z, Z-A, most or least played. Clicking a
card opens a popup with all its songs.

Screenshots of the other states only differ in the top bar, and the
layout baseline is taken again with the new statistics states.
This commit is contained in:
Sansan committed 2026-10-06 23:33:00 +02:00
1 parent f7619baf34
commit a21b6e9b22
30 files changed
+25379 -2306

No files matched your search

+56 -4
View File
@@ -1,7 +1,8 @@
class_name UiTour
extends RefCounted
## Walks the main window through a fixed set of states against a fake MusicServer:
## startup, players added, a song playing, every popup open and a sync.
## startup, players added, a song playing, every popup open, a sync and the statistics
## tab.
##
## Used for screenshots ([code]tools/take_screenshots.gd[/code]) and for the layout
## test, so both look at exactly the same states. Nodes are looked up by name in the
@@ -38,7 +39,6 @@ const POPUPS: Array[Array] = [
["SettingsWindow", "SettingsPopup"], ["QRWindow", "QRPopup"],
["NewCharacterSelect", "CharacterSelectPopup"], ["ShortcutsWindow", "ShortcutsPopup"],
["AboutWindow", "AboutPopup"], ["WinnerWindow", "WinnerPopup"], ["Log", "LogPopup"],
["StatisticsPopupPanel", "StatisticsPopupPanel"],
]
var _main: Control
@@ -68,9 +68,37 @@ static func make_server() -> FakeHttpTransport:
server.respond_bytes("/music/played?song=0", PackedByteArray())
server.respond_bytes("/sync", PackedByteArray())
server.respond_json("/sync/progress", SYNC_PROGRESS)
server.respond_json("/statistics/soundtracks", soundtrack_statistics())
return server
## Play statistics for every soundtrack in [constant SOUNDTRACKS], the same on every
## run. Some have never been played.
static func soundtrack_statistics() -> Array[Dictionary]:
var statistics: Array[Dictionary] = []
for i: int in SOUNDTRACKS.size():
var played: int = (i * 7) % 11
var songs: Array[Dictionary] = []
for song: int in 5:
songs.append({
"song_uuid": "song-%d-%d" % [i, song],
"soundtrack_uuid": "soundtrack-%d" % i,
"soundtrack_name": SOUNDTRACKS[i],
"song_name": "Track %d" % (song + 1),
"times_played": maxi(0, played - song * 2),
})
var soundtrack: Dictionary = {
"soundtrack_uuid": "soundtrack-%d" % i,
"soundtrack_name": SOUNDTRACKS[i],
"soundtrack_played": played,
"songs": songs,
}
if played > 0:
soundtrack.soundtrack_last_played = "2026-09-%02dT20:00:00Z" % (i + 1)
statistics.append(soundtrack)
return statistics
## The pictures in [constant CHARACTERS_DIR], sorted by name.
static func character_names() -> Array[String]:
var names: Array[String] = []
@@ -162,6 +190,32 @@ func run(on_state: Callable) -> void:
sync_popup.close()
await _settle()
var statistics_view: StatisticsView = _node("StatisticsView")
_main.show_tab(&"statistics")
await _settle()
await on_state.call("statistics", statistics_view)
statistics_view.show_option.select(StatisticsView.ShowFilter.NEVER_PLAYED)
statistics_view.apply_filter()
await _settle()
await on_state.call("statistics_never_played", statistics_view)
statistics_view.show_option.select(StatisticsView.ShowFilter.ALL)
statistics_view.sort_option.select(StatisticsView.SortOrder.MOST_PLAYED)
statistics_view.apply_sort()
statistics_view.search_field.text = "chrono"
statistics_view.apply_filter()
await _settle()
await on_state.call("statistics_search", statistics_view)
var stats_popup: SoundtrackStatsPopup = _node("SoundtrackStatsPopup")
for card: SoundtrackCard in statistics_view.cards:
if card.visible:
stats_popup.show_soundtrack(card.soundtrack)
break
await _settle()
await on_state.call("popup_SoundtrackStats", stats_popup)
stats_popup.close()
_main.show_tab(&"quiz")
await _settle()
## Returns where every Control and Window in [param subtree] is on screen, keyed by its
## path from [param main]: [code]{"rect": [x, y, width, height], "visible": bool}[/code].
@@ -270,8 +324,6 @@ func _open_popup(popup_name: String) -> Node:
popup.show_grid("Crono")
"WinnerPopup":
popup.show_winner(Settings.player_array[0])
"StatisticsPopupPanel":
_call("get_statistics")
_:
popup.open()
return popup