Feature: Person activity timeline (/people/:id/timeline)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 35/100
Direzione di ricerca
Inizia con il PersonCommentAggregator esistente, app/presenters/activity_presenter.rb, admin/ahoy_activities_controller.rb:292 e i percorsi delle viste della timeline delle persone descritti nell’issue. Esegui o aggiungi le spec di service, presenter, request e system che coprano l’unione degli stream, la paginazione tramite cursore, l’attribuzione, i filtri e il rendering del sentinel. Il lavoro è completo quando la timeline intercalata viene paginata senza duplicati e applica l’ambito e il comportamento di accesso indicati.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Context
Build a per-person activity timeline at /people/:id/timeline — one chronological
feed of everything that happened to/around a person: profile updates, scholarship
awarded, registration created, payment applied, membership renewed, account logins,
tagging changes, plus the person's internal comments and communications.
This evolves an in-progress /people/:id/communications page (which interleaves a
person's comments + communications/notifications) — that work is renamed to
timeline and broadened into a full activity feed.
Feasible because change history already exists: AhoyTrackable is mixed into
ApplicationRecord, so every model auto-writes create.*/update.*/destroy.* rows
to ahoy_events with before/after diffs in properties["changes"], plus auth.*
account events on User. PaperTrail additionally covers financial models. So the work
is mostly assembly of existing data + presentation.
Scope decisions (confirmed)
- Sources: everything — all lifecycle events (incl. profile edits, tagging,
subscriptions) + comments + communications. - Rows: mixed — comments stay editable-in-place, communications stay rich
cards, and ahoy changes render as lightweight event rows (icon + label + diff). - Filter: category chips (All / Changes / Comments / Communications) + a
subject/content keyword search. - Authorship rule: credit records by
author_id, falling back tocreated_by_id
only when the model has noauthor_id— so an admin isn't credited for records that
have a real author. (Mirrors the existingPersonCommentAggregatorstory logic.) - Imports/jobs creates must be included (console edits may be ignored).
- Infinite scroll — must not load all of a person's records/events per request.
Event sourcing (two change streams)
- "Created" rows derive from each record's own
created_at, NOT ahoy — ahoy
create.*only fires for logged-in web actions, so imports/jobs/console creates would
be missed. Every row hascreated_atregardless of origin → complete create coverage.
Ahoycreate.*events are therefore excluded to avoid double-counting. - "Updated"/"deleted" rows come from
ahoy_events(update.*/destroy.*+
auth.*/taggings.*) — web-only. Console/job/import edits are out of scope
(accepted). - PaperTrail
versionsintentionally not merged into the feed (avoid double rows /
second code path); stays available for a future financial drill-down. - Limitation: update/delete rows only go back to when ahoy tracking was enabled; create
rows go back as far as the records themselves.
Technical approach
Service PersonTimeline (evolves person_communication_feed.rb) — exposes
#page(before:, limit: 20) => { items:, next_cursor: }. k-way-merges four streams,
each queried keyset-style (WHERE <ts> < before ORDER BY <ts> DESC LIMIT limit),
merges the ≤limit-per-stream rows in Ruby, emits the top limit, derives next_cursor
from the last row:
- Comments —
PersonCommentAggregator#comments(reuse). - Communications —
Notification.email(person.communications_email). - Creates — resolve the person's directly-associated records across ~20 source
types (FK: registrations, scholarships, memberships + invoices, licenses,
affiliations, form submissions, event staff, CE, payments; polymorphic: addresses,
contact methods, tags, sectors, bookmarks, the person; authored/created content via
the authorship rule) → oneTimelineChange(action: :created, occurred_at: created_at, actor: author-or-created_by)each. - Updates/deletes —
Ahoy::Eventper{resource_type => ids}map (promoted
columns), nameREGEXP '^(update|destroy|auth|taggings)\.'→TimelineChange. subject/contentfilters +before/limitpushed into each stream's SQL so the
per-streamLIMITstays honest. Category chip selects which streams run.
Presenter — reuse/extend app/presenters/activity_presenter.rb ("The ONE true
timeline") for ahoy rows; add label ("<Subject> <verb>", auth.* special-cased),
icon+theme (new per-model FA-glyph map keyed to DomainTheme colors), detail_lines
(from properties["changes"]), actor_name ("System / import" when nil), link.
Pagination — cursor/keyset infinite scroll (net-new; no existing pattern):
composite (occurred_at, key) cursor, mirroring the comparator at
admin/ahoy_activities_controller.rb:292. UI = a bottom sentinel turbo_frame_tag "timeline_after_<cursor>", loading: :lazy that fetches the next page (same frame id →
replaces itself with next rows + next sentinel, appending visually). Reuse
prefetch_lazy_controller.js (IntersectionObserver) to load ~500px early. Filter
change re-navigates the outer person_timeline_results frame → fresh first page.
will_paginate/tailwind_paginate are NOT used here (can't express a cursor).
Views: people/timeline.html.erb (shell: header, chips + keyword form, lazy
frame), people/person_timeline_results.html.erb (rows + sentinel), _timeline_feed
(branch on class: Comment → editable comment; Notification → comm card; TimelineChange
→ new _timeline_event_row).
Reuse: PersonCommentAggregator (id-resolution), users_controller#account_events_for
users/sections/_account_activity(ahoy diff rendering),ActivityPresenter,
lib/domain_theme.rb,application_helper#section_icon_class,config/initializers/ahoy.rb.
Testing
- Service spec: interleave order; keyset paging (
before/next_cursor, no
overlap, nil at end); nocreate.*ahoy dup; import create (nil actor) appears;
authorship rule (story authored by someone else but created_by this user → excluded;
author-nil + created_by-this-user → included); category/subject/content filters;
unrelated person excluded. - Presenter spec: label/icon/detail_lines/actor_name for created/updated/destroyed
- auth.* rows.
- Request spec: shell renders chips + frame; first page returns interleaved rows +
sentinel; sentinelbefore=URL returns next page w/ no dup; last page omits
sentinel; filters; non-admin → root; ahoy view-tracking on full page only. - Light system spec: scroll appends later rows.
Rough effort
~4–5 focused days. Widest surface: create-derivation across ~20 source types with the
authorship scoping. Main correctness risks: the authorship rule, cursor tie/de-dup at
page boundaries, and per-stream filter-in-SQL keeping LIMIT honest.
🤖 From Claude: drafted from a planning session; scope decisions above were confirmed with the requester.
- Lingua principale
- Ruby
- Stelle
- 15
- Fork
- 28
- Merge medio
- 1g 12m
- PR unite (30g)
- 262
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Include un Dockerfile o un file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di rubyforgood/awbw
-
blocked
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
rubyforgood/awbw#2824 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
rubyforgood/awbw#2822 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
rubyforgood/awbw#2160 ·
I maintainer di solito rispondono entro 1 giorno
-
Update callout faq defualt text when the new person subscription launchesForse già presa @maebeale l’ha presa 82 giorni fa. Apertablocked
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
rubyforgood/awbw#2095 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
rubyforgood/awbw#2088 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di rubyforgood/awbw
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
account: profile
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 5 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
bug good first issue help wanted
Difficoltà 2/5 1-3 ore Idoneità per principianti 90/100
ysb3996-blip/oops#24 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100