Skip to content

Add user presence tracking and online status visibility - #884

Open
aXenDeveloper wants to merge 3 commits into
canaryfrom
claude/user-presence-system-g531gn
Open

aXenDeveloper wants to merge 3 commits into
canaryfrom
claude/user-presence-system-g531gn

Conversation

@aXenDeveloper

Copy link
Copy Markdown
Owner

Description

What?

This PR implements a comprehensive user presence system that tracks when users are online and last seen, with privacy controls to hide online status if desired.

Key features:

  • Presence tracking: Records lastSeenAt timestamp and hideOnlineStatus privacy flag on users
  • Heartbeat mechanism: Frontend sends periodic heartbeats (every 60 seconds) to keep presence updated
  • Batch presence queries: Efficient endpoint to fetch presence for multiple users at once
  • Privacy controls: Users can hide their online status in account settings
  • Permission-based visibility: Staff with can_view_presence permission can see hidden statuses
  • Admin interface: New presence card in user detail view showing online status and last activity
  • Profile integration: Display online status on user profiles with appropriate privacy filtering
  • React Query integration: Query options and hooks for presence data with proper caching

New API endpoints:

  • POST /api/users/presence/heartbeat - Update user presence
  • POST /api/users/presence/batch - Fetch presence for multiple users
  • PATCH /api/users/privacy - Update privacy settings
  • GET /api/admin/users/:id/presence - Admin endpoint for user presence

New UI components:

  • UserPresenceStatus - Displays online/offline status with last seen time
  • Privacy settings in account overview
  • User presence card in admin user detail view

Why?

User presence is a fundamental feature for community platforms, allowing members to see who's online and when others were last active. This implementation provides:

  • Better user engagement by showing activity status
  • Privacy-respecting design with opt-out capability
  • Efficient data fetching with batch queries and caching
  • Admin visibility for moderation purposes
  • Extensible architecture for plugins to display presence information

The heartbeat approach keeps presence fresh without requiring persistent connections, and the batch endpoint prevents N+1 queries when displaying presence for multiple users.

https://claude.ai/code/session_0198jw2ioVCHrx7GaSPcZND3

…e anonymously

- `core_users.lastSeenAt` and `core_users.hideOnlineStatus` with a migration
- `UserPresenceModel`: per-session leases in a Redis sorted set (one per
  member, each session expiring on its own), bulk reads in one query and
  one pipeline, throttled forward-only `lastSeenAt` writes; without Redis
  presence falls back to `lastSeenAt` within the lease window
- `CacheModel` lease-set primitives: `renewLease`, `releaseLeases`,
  `countLeases`, `isConfigured`
- API: `POST /users/presence/heartbeat`, `GET /users/{id}/presence`,
  `POST /users/presence/batch`, `PUT /users/me/privacy` and
  `GET /admin/users/{id}/presence`, all `private, no-store`
- `users:can_view_hidden_presence` for admins and moderators; one privacy
  resolver for every read
- sign-out and device revocation end only that session's lease
- client: visibility-aware heartbeat shared across tabs, profile indicator,
  "Browse anonymously" setting, AdminCP presence card and a reusable
  `UserPresenceStatus` component

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0198jw2ioVCHrx7GaSPcZND3
@github-actions

Copy link
Copy Markdown

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Add user presence tracking and online status visibility". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T20:33:26.595184Z e61287b PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown

React Doctor found 1 new issue in 1 file · 1 warning · score 90 / 100 (Great) · 0 fixed · vs canary

1 warning

src/views/auth/settings/overview/privacy-settings.tsx

  • ⚠️ L51 Loading flag reset outside finally no-loading-flag-reset-outside-finally

Reviewed by React Doctor for commit e61287b. See inline comments for fixes.

onCheckedChange={async checked => {
setIsSaving(true);
const result = await onUpdate({ hideOnlineStatus: checked });
setIsSaving(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

React Doctor · react-doctor/no-loading-flag-reset-outside-finally (warning)

This resets a loading/busy flag only on the success path: if the awaited call rejects the reset never runs and the flag stays stuck truthy (a spinner that never stops, a button disabled forever). Move the reset into a finally block, or mirror it on every catch, so it clears on rejection too.

Fix → A trailing setLoading(false) after an await never runs if the awaited call rejects, so the flag stays stuck truthy; reset it in a finally block (or mirror the reset on every catch) so it clears on both paths.

Docs

This branch has not been deployed

No deployments
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.

2 participants