Feature: Person activity timeline (/people/:id/timeline)
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 35/100
Hướng nghiên cứu
Bắt đầu với PersonCommentAggregator hiện có, app/presenters/activity_presenter.rb, admin/ahoy_activities_controller.rb:292 và các đường dẫn view của people timeline được mô tả trong issue. Chạy hoặc thêm các spec của service, presenter, request và system để bao quát việc hợp nhất các stream, phân trang bằng cursor, quyền tác giả, các bộ lọc và việc render sentinel. Được coi là hoàn tất khi timeline đan xen được phân trang mà không có bản ghi trùng lặp và thực thi scope cùng hành vi truy cập đã nêu.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- Ruby
- Star
- 15
- Fork
- 28
- Merge trung bình
- 1 ngày 12 phút
- Pull request đã merge (30 ngày)
- 262
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Có Dockerfile hoặc tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của rubyforgood/awbw
-
blocked
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
rubyforgood/awbw#2824 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
rubyforgood/awbw#2822 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
rubyforgood/awbw#2160 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Update callout faq defualt text when the new person subscription launchesCó thể đã có người làm @maebeale đã nhận 79 ngày trước. Đang mởblocked
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
rubyforgood/awbw#2095 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
rubyforgood/awbw#2088 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của rubyforgood/awbw
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
hackclub/hcb#15454 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
voxpupuli/puppet-archive#566 ·
-
area/tech debt
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
Maintainer thường phản hồi trong vòng 2 ngày
-
verified
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 67/100
Maintainer thường phản hồi trong vòng 5 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 86/100
akitaonrails/ai-book-2026#10 ·