Skip to content

Try: Fediverse Wrapped statistics feature#2633

Draft
pfefferle wants to merge 55 commits intotrunkfrom
try/fediverse-wrapped-statistics
Draft

Try: Fediverse Wrapped statistics feature#2633
pfefferle wants to merge 55 commits intotrunkfrom
try/fediverse-wrapped-statistics

Conversation

@pfefferle
Copy link
Member

@pfefferle pfefferle commented Dec 15, 2025

Fixes #2597

Screenshot 2026-01-27 at 17 13 32

Proposed changes:

  • Implements Fediverse statistics dashboard widget using React/TypeScript
  • Statistics class for collecting engagement metrics (likes, reposts, comments)
  • REST API endpoint for fetching statistics data
  • Dashboard widget showing:
    • Stat highlights (followers, posts, likes, reposts) with year-over-year comparison
    • Interactive line chart for monthly engagement
    • Chart legend showing engagement types
    • Top supporter section
    • Top posts section
  • Support for stored monthly stats (for historical data)
  • CLI command for populating demo data (local dev only)

Removed from original scope:

  • Shareable HTML wrapped card (will be a separate block later)
  • Annual email template

Other information:

  • Have you written new tests for your changes, if applicable?

Testing instructions:

Setup demo data (optional):

npx wp-env run cli wp activitypub stats populate

Dashboard Widget:

  1. Go to WordPress Dashboard (/wp-admin/)
  2. Look for "Fediverse Stats" widget
  3. If multiple actors enabled, use the selector to switch between user/blog stats
  4. Verify:
    • Stat highlights show followers, posts, likes, reposts counts
    • Line chart displays monthly engagement data
    • Legend shows engagement types with colors
    • Top supporter section (if data exists)
    • Top posts section (if data exists)

Clear demo data:

npx wp-env run cli wp activitypub stats clear

Changelog entry

  • Automatically create a changelog entry from the details below.
Changelog Entry Details

Significance

  • Patch
  • Minor
  • Major

Type

  • Added - for new features
  • Changed - for changes in existing functionality
  • Deprecated - for soon-to-be removed features
  • Removed - for now removed features
  • Fixed - for any bug fixes
  • Security - in case of vulnerabilities

Message

Add Fediverse statistics dashboard widget with engagement metrics and charts.

Implements monthly/annual statistics collection and display:
- Core Statistics class for collecting engagement metrics
- Scheduler for monthly stats collection and annual compilation
- Dashboard widget showing stats with charts
- Annual email template for year-end summaries
- Shareable wrapped card for public sharing

Known issues:
- URL rewrite rules for wrapped card need debugging (year parameter not captured)
- Stats display shows 0 values on HTTP requests despite correct query var registration

Related to #2597
@pfefferle pfefferle added the Skip Changelog Disables the "Changelog Updated" action for PRs where changelog entries are not necessary. label Dec 15, 2025
@pfefferle pfefferle self-assigned this Dec 15, 2025
@pfefferle pfefferle added the Skip Changelog Disables the "Changelog Updated" action for PRs where changelog entries are not necessary. label Dec 15, 2025
@pfefferle pfefferle requested a review from a team December 15, 2025 15:27
Eliminated the Fediverse Wrapped shareable card functionality by deleting the template, removing related rewrite rules, query vars, and rendering logic from Statistics, and cleaning up associated JavaScript and dashboard UI elements.
- Replace jQuery-based widget with React components
- Add REST API endpoint for statistics data
- Add TypeScript types for stats data structures
- Add chart legend showing engagement types
- Support stored monthly stats for demo/historical data
- Add CLI command for populating demo data (local dev only)
@github-actions github-actions bot added [Feature] REST API [Focus] Editor Changes to the ActivityPub experience in the block editor labels Dec 17, 2025
- Make StatHighlights display engagement types dynamically from API
- Add period label "This month vs. last year" to clarify comparison
- Draw individual lines for each engagement type in the chart
- Use WordPress default color palette CSS variables with fallbacks
- Remove hardcoded comment types - all types now dynamic
- Move demo data generation from Statistics class to local CLI
Copy link
Member

@obenland obenland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it to structure, I'd see if there are stats we can bump as they occur instead of aggregating monthly. I reckon that'll probably be more accurate, especially for things we can't or are hard to query after the fact.

One of the reasons Jetpack is so powerful is because it can store large amounts of data, process them remotely, and aggregate them. It might be worth considering making this a jetpack integration instead of hoping an option is enough?

The dashboard widget is neat, is there something we can do to make that a bit more interactive?

@pfefferle
Copy link
Member Author

@obenland thanks for your feedback! 🫶

@pfefferle
Copy link
Member Author

One of the reasons Jetpack is so powerful is because it can store large amounts of data, process them remotely, and aggregate them. It might be worth considering making this a jetpack integration instead of hoping an option is enough?

I somehow agree, that we might be able to have a collab, but you checked the number of folks that have both installed an that is quite low. So I would love to have at least a basic stats feature in the plugin, to maybe motivate users to interact a bit more!?

- Move cron schedule methods (add_cron_schedules, register_schedules,
  deregister_schedules) from Scheduler\Statistics to main Scheduler
- Move helper methods (get_next_first_of_month, get_next_january_first)
  to main Scheduler
- Update dashboard stats widget to get actors on JS side like the reader
- Use @wordpress/core-data to check actor mode and capabilities
- Remove PHP-passed actors from Statistics_Dashboard
Resolved conflicts:
- includes/class-migration.php: Added both emoji migration and stats backfill callbacks
- includes/class-scheduler.php: Combined SCHEDULES constant loop with stats scheduling

Additional changes based on PR feedback:
- Changed annual stats notification from January 1st to December 1st (per @obenland)
- Fixed CSS linting issues in dashboard-stats stylesheet
- Changed compile_and_send_annual_stats to get current year instead of
  previous year (since it now runs December 1st, not January 1st)
- Updated docblock to reflect the December timing
- Added TODO for shareable landing page feature (per @obenland feedback)
- Use WordPress admin color palette (#2c3338, #646970, #f6f7f7, etc.)
- Add dashicons for stat types (followers, posts, likes, reposts, comments)
- Match padding and spacing from #dashboard_right_now widget
- Use border-top separators instead of background color blocks
- Style top supporter section like WordPress admin notices
- Style top posts section like activity widget list items
- Add responsive breakpoint for mobile
- Make chart container full width with background extending to edges
- Better align icons with text using flex-start and margin adjustments
- Simplify stat-content to inline display for natural text flow
Change all section headlines from h4 to h3 for proper heading
hierarchy. Move the "Engagement Over Time" title outside the grey
background area by applying the background only to the chart container.
Change "This month vs. last year" from p to h3 for consistent
heading hierarchy across all widget sections.
Match WordPress admin dashboard CSS patterns:
- Centralize h3 styles (14px, font-weight 400)
- Use WordPress admin color palette (#50575e, #82878c)
- Match padding patterns from #dashboard_right_now
- Simplify section styling for cleaner appearance
- Use float layout for stat items (matches #dashboard_right_now li)
- Icon styling exactly matches WordPress: font, padding, display, vertical-align
- Activity block pattern for sections with negative margins
- Exact padding values from WordPress dashboard CSS
- h3 margins match #dashboard-widgets h3 pattern
- Current month stats are now always queried live to include recent
  engagement (likes, reposts, comments)
- Backfill migration now skips the current month since it's in progress
- Previous months use stored stats for performance
- Add delete_monthly_stats() and recollect_monthly_stats() methods
- Add force option to trigger_monthly_collection() scheduler method

This fixes an issue where new reposts wouldn't show in stats because
the current month data was being served from cached options.
Expose the scheduler's trigger_monthly_collection() and
trigger_annual_compilation() methods via the local development CLI:

- stats collect: Trigger monthly stats collection
- stats compile: Trigger annual stats compilation

Options include --year, --month, --force, and --no-email for flexibility.
- Remove get_yearly_monthly_breakdown() (replaced by get_rolling_monthly_breakdown)
- Remove deprecated get_year_comparison() (use get_period_comparison instead)
- Remove trigger_monthly_collection() from scheduler
- Remove trigger_annual_compilation() from scheduler
- Remove delete_monthly_stats() and recollect_monthly_stats() from Statistics
- Implement collect and compile logic directly in local CLI class
The queries were defaulting to only 'post' type, missing engagement
on pages and custom post types enabled for ActivityPub.

Now uses activitypub_support_post_types option in all queries:
- count_engagement_in_range()
- get_top_posts()
- get_top_multiplicator()
- get_earliest_data_year()
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a "Fediverse Wrapped" statistics feature for the ActivityPub plugin, providing users with engagement analytics similar to Spotify Wrapped. The feature includes a React-based dashboard widget displaying follower growth, post metrics, and engagement statistics with month-over-month comparisons and trend visualization.

Changes:

  • Adds comprehensive statistics collection and storage system with monthly/annual aggregation
  • Implements REST API endpoint for fetching statistics data with permission checks
  • Creates interactive React dashboard widget with stat highlights, line charts, top posts, and top supporter sections
  • Adds WP-CLI commands for demo data population and statistics management
  • Includes scheduled jobs for automatic monthly statistics collection and annual compilation
  • Provides email notification system for annual "wrapped" summary

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 21 comments.

Show a summary per file
File Description
includes/class-statistics.php Core statistics collection class with methods for aggregating engagement metrics, follower counts, and top content
includes/rest/class-statistics-controller.php REST API controller providing authenticated endpoint for retrieving statistics data
includes/wp-admin/class-statistics-dashboard.php Dashboard widget registration and asset enqueuing for the statistics display
includes/scheduler/class-statistics.php Scheduled task handlers for monthly/annual statistics collection and email notifications
includes/class-scheduler.php Cron schedule registration for monthly and annual statistics compilation
includes/class-migration.php Migration hook to backfill historical statistics data on plugin update
includes/collection/class-followers.php Added count_in_range method to support follower growth tracking
local/class-cli.php WP-CLI commands for statistics management including populate, clear, collect, and compile actions
activitypub.php Plugin initialization updates to register new statistics classes and REST controller
src/dashboard-stats/ Complete React/TypeScript implementation of the dashboard widget UI with components for highlights, charts, top posts, and supporters
templates/emails/annual-wrapped.php HTML email template for annual statistics summary notifications
build/dashboard-stats/ Compiled JavaScript and CSS assets for the dashboard widget

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix annual email guard to check all registered comment types dynamically
  instead of hardcoded plural keys (likes_count vs like_count)
- Fix follower growth calculation to use followers_count (gained) instead
  of non-existent followers_gained/followers_lost keys
- Add comment date filter to get_top_posts() to only count engagement
  within the specified date range
- Use get_comment_types_for_stats() in count_engagement_in_range() and
  get_period_comparison() to include federated comments in totals
- Fix dashboard widget to fall back to user stats when blog stats are
  forbidden (non-admin users)
- Fix December scheduling to correctly check if past Dec 1st 3:00 AM
- Add generic Mailer::send() method for email dispatching
- Change "Followers" to "New Followers" in dashboard highlights (clarity)
- Add SVG accessibility (role="img", aria-labelledby, title element)
- Add aria-label for external links (opens in new tab indicator)
- Use deterministic color assignment via djb2 hash for chart lines
- Delay statistics backfill by 1 hour to avoid immediate load after upgrade
Register collect and compile as proper subcommands following the
existing CLI structure pattern.
Move the stats endpoint under the admin subfolder and update the
rest_base to admin/stats to match the admin controller pattern.
- Register Scheduler\Statistics::init() so cron callbacks work
- Add import section comments to all dashboard-stats TSX files
- Fix auto-generated @param root0 JSDoc with proper annotations
- Add ReactNode return type to all component functions
- Fix missing backslash on current_user_can in dashboard template
…I send command

- Make Mailer::send() accept an optional $alt_body parameter; auto-generate
  plain text from HTML via wp_strip_all_tags() when not provided
- Remove tightly-coupled get_plain_text_body() from Mailer; move plain text
  generation into the Statistics scheduler caller
- Add activitypub_mailer_annual_report user/blog preference with checkbox
  in both user profile and blog settings notification fieldsets
- Fix esc_html() misuse on %d format specifier in annual-wrapped template
- Add `wp activitypub stats send` CLI subcommand for testing the annual
  report email without waiting for cron
- Make Statistics::send_annual_email() public for CLI access
- Cast user_id to int in Statistics_Controller to fix strict comparison
  with BLOG_USER_ID that broke blog actor stats
- Replace unbounded get_posts(-1) with SQL subqueries to prevent OOM on
  large sites; extract get_post_ids_subquery() helper
- Add 15-minute transient caching to stats REST endpoint
- Fix email template using esc_html__() with HTML arguments (wp_kses)
- Fix timezone mismatch in date construction (gmmktime instead of
  strtotime/gmdate mix)
- Reschedule monthly cron to exact first-of-month to prevent 30-day drift
- Sanitize template path in Mailer::send() to prevent path traversal
- Guard against duplicate 'comment' key in get_comment_types_for_stats()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] CLI [Feature] Collections [Feature] Notifications [Feature] REST API [Feature] WP Admin [Focus] Editor Changes to the ActivityPub experience in the block editor Skip Changelog Disables the "Changelog Updated" action for PRs where changelog entries are not necessary. [Status] In Progress

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Fediverse Wrapped - Annual Blog Summary (converted to project)

3 participants