Feature: Person activity timeline (/people/:id/timeline)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 35/100
Línea de trabajo
Empieza con el PersonCommentAggregator existente, app/presenters/activity_presenter.rb, admin/ahoy_activities_controller.rb:292 y las rutas de la vista de la línea de tiempo de personas descritas en el issue. Ejecuta o añade las specs de service, presenter, request y system que cubran la combinación de streams, la paginación por cursor, la autoría, los filtros y el renderizado del sentinel. Se considera terminado cuando la línea de tiempo intercalada pagina sin duplicados y aplica el alcance y el comportamiento de acceso indicados.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- Ruby
- Estrellas
- 15
- Forks
- 28
- Merge medio
- 1 d 12 min
- PR fusionados (30 d)
- 262
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de rubyforgood/awbw
-
blocked
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
rubyforgood/awbw#2824 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
rubyforgood/awbw#2822 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
rubyforgood/awbw#2160 ·
Los mantenedores suelen responder en 1 día
-
Update callout faq defualt text when the new person subscription launchesPosiblemente ocupada @maebeale la tomó hace 79 días. Abiertoblocked
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
rubyforgood/awbw#2095 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
rubyforgood/awbw#2088 ·
Los mantenedores suelen responder en 1 día
Todos los issues de rubyforgood/awbw
Issues similares
-
verified
Dificultad 2/5 1-3 horas Aptitud para principiantes 67/100
Los mantenedores suelen responder en 5 días
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 86/100
akitaonrails/ai-book-2026#10 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
TheOdinProject/curriculum#31495 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
zerocracy/judges-action#2984 ·
Los mantenedores suelen responder en 1 día