Skip to content

In-app listening stats, streaks, monthly recap popup, and per-song cloud sync#361

Merged
LargeModGames merged 1 commit into
mainfrom
feature/stats-streaks-recap
Jul 10, 2026
Merged

In-app listening stats, streaks, monthly recap popup, and per-song cloud sync#361
LargeModGames merged 1 commit into
mainfrom
feature/stats-streaks-recap

Conversation

@LargeModGames

Copy link
Copy Markdown
Owner

What's new

  • Stats screen (Library sidebar entry): period tabs (7d / 30d / month / year / all), top tracks/artists/albums, last-10-days bars, and playable top tracks (Enter starts playback for Spotify tracks). Cycle periods with [ / ].
  • Listening streaks: current/longest streak and time listened today, shown on the Home screen and in the Stats summary. Maintained incrementally by the history collector, so no file re-reads per track.
  • Monthly recap popup: when a recap is due (30 days since the last one), a prompt offers to open the generated share page. Dismissable with Esc ("later") or d (never again, persisted; re-enable in Settings). Never steals focus from an input field.
  • Offline share card: the recap HTML page renders a downloadable 440x660 share card with a pure canvas renderer (no external JS), toggleable between the requested period and All Time.
  • Per-song cloud sync: finished listens now push to spotatui.com as each song completes instead of only at open/close, so long sessions no longer stall the exit sync. An in-flight guard prevents overlapping syncs from double-sending; the exit sync remains as the backstop for the final track.

Implementation notes

  • Recap generation runs through IoEvent::GenerateRecap on the network layer with spawn_blocking; the UI thread never blocks on export.
  • The RecapPrompt popup is wired into all modal mechanisms (input-mode gate, escape handling, mouse exclusion) like the other prompts.
  • Streaks are computed from per-day totals kept in memory by the collector; the listens file is parsed once at startup instead of after every track.

Testing

  • cargo fmt, cargo clippy --no-default-features --features telemetry -- -D warnings, and cargo test --no-default-features --features telemetry (409 tests) all pass.

…fline share card, and per-song cloud history sync
@LargeModGames LargeModGames merged commit 0405dde into main Jul 10, 2026
15 checks passed
@LargeModGames LargeModGames deleted the feature/stats-streaks-recap branch July 10, 2026 12:58
LargeModGames added a commit that referenced this pull request Jul 10, 2026
…avigation, and custom screens (#363)

## What's new

A major expansion of the Lua plugin API (v4 shipped in #302), made this
a while ago but forgot to merge it:

- **Async data reads**: `get_playlists`, `get_queue`,
`get_search_results`, `get_saved_tracks`/`albums`/`shows`,
`get_recently_played`, `get_devices`, `get_lyrics`, all with the `(data,
err)` callback convention.
- **More actions**: `set_repeat`/`cycle_repeat`, `transfer_playback`,
`play_uri`/`play_context`, `add_to_queue`, playlist create/edit, and
save/follow actions.
- **Timers**: `set_timeout`, `set_interval`, `cancel_timer`.
- **Per-plugin storage**: JSON-backed
`storage_get`/`set`/`remove`/`keys`.
- **Navigation**: `navigate`, `back`, `current_route`, plus a `config()`
read of the user config.
- **New events**: `shuffle_change`, `repeat_change`, `device_change`,
`search_results`, `route_change`.
- **Custom screens**:
`register_screen`/`set_screen`/`show_screen`/`close_screen` with
paragraph/list/gauge widgets and per-key callbacks (new
`RouteId::PluginScreen` / `ActiveBlock::PluginScreen`,
`src/tui/ui/plugin_screen.rs`, example plugin
`examples/plugins/queue-browser.lua`).

Documented in `docs/scripting.md`.

## Notes

- Rebased onto current main (post #360 and #361); the new Stats and
RecapPrompt routes got plugin-facing names in the exhaustive
`route_name` map.
- `get_recently_played` gained a `navigate` flag so plugin data reads
don't hijack the current screen; the handler path still navigates as
before.

## Testing

- `cargo fmt`, `cargo clippy -- -D warnings` (with and without
`scripting`), and `cargo test --no-default-features --features
telemetry,scripting` (572 tests) all pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant