Skip to content

Sort 'Latest Portfolios' by recency + relative timestamp#32

Merged
fleveque merged 4 commits into
mainfrom
feat/recent-portfolios
May 21, 2026
Merged

Sort 'Latest Portfolios' by recency + relative timestamp#32
fleveque merged 4 commits into
mainfrom
feat/recent-portfolios

Conversation

@fleveque

Copy link
Copy Markdown
Owner

Summary

The card on `/` listed portfolio slugs in non-deterministic order (whatever order `DynamicSupervisor.which_children` returned). Effectively random — users couldn't tell which portfolios had recent activity. Now sorted by most-recently-updated, capped at top 5, with a compact relative timestamp.

How

  • PortfolioWorker: new `updated_at` field on the defstruct, stamped on every `:update_holdings` cast. Stays nil on init so an idle worker (e.g. resumed from a stale DETS row) doesn't pretend to be freshly active.
  • Store: persists `updated_at` alongside the other rehydrate fields. On restore, the orchestrator passes the original timestamp back via a `__updated_at` payload override so a server restart doesn't make every portfolio look new.
  • DashboardAggregator: new `recent_portfolios` derived field — top 5 sorted by `updated_at` desc, excluding workers with no timestamp yet.
  • DashboardLive: template renders the new list with a small `relative_time/1` helper. Strings are gettext'd: just now, Nm/h/d/w ago.

33 tests / 0 failures.

Test plan

  • `mix test` — green
  • `mix format`, `mix compile` clean
  • Manual: open `/`, watch the order change as portfolios update in Quantic; freshly-updated should jump to the top
  • Manual: restart Pulse → list survives with original timestamps preserved (no "every portfolio just now")

fleveque added 4 commits May 21, 2026 17:33
The card on / listed slugs in non-deterministic order (DynamicSupervisor
child order) which was effectively random — users couldn't tell which
portfolios had recent activity. Now sorted by most-recently-updated and
labelled with a compact relative timestamp.

- PortfolioWorker: new `updated_at` field on the defstruct, stamped on
  every `:update_holdings` cast and on init (nil) so an idle worker
  doesn't show up as freshly active.
- Store: persists `updated_at` alongside holdings / base_currency /
  stats, and feeds it back through a `__updated_at` payload override on
  restore so a server restart doesn't make every portfolio look new.
- DashboardAggregator: new `recent_portfolios` derived field — top 5
  sorted by `updated_at` desc, excluding workers that haven't received
  a payload yet.
- DashboardLive: template renders the new list with a small
  `relative_time/1` helper (just now / Nm / Nh / Nd / Nw ago, gettext'd).

33 tests / 0 failures.
The original gate (Enum.reject is_nil) wiped the list every time the
server restarted: workers restored from pre-`updated_at` DETS rows had
no timestamp yet, so the empty-state took over until each portfolio
got a fresh Quantic broadcast.

Sort by updated_at desc but keep nil-timestamp portfolios at the
bottom of the list — they still appear, just without a label
('—' in the template) until their next update.
The card label was misleading once the sort criterion changed: it's
ordered by `updated_at` desc, so "Recently Updated" is the honest name.
Also bump Popular Stocks / Recently Updated / Trending This Week to a
consistent max of 10 entries each (popular was already 10; recent went
5 → 10; trending went 5 → 10).
@fleveque fleveque merged commit 57b2eb5 into main May 21, 2026
1 check passed
@fleveque fleveque deleted the feat/recent-portfolios branch May 21, 2026 16:00
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