Skip to content

Comments

Add posthog capture for optimistic actions#1521

Merged
ahmetskilinc merged 2 commits intostagingfrom
06-27-add_posthog_capture_for_optimistic_actions
Jul 1, 2025
Merged

Add posthog capture for optimistic actions#1521
ahmetskilinc merged 2 commits intostagingfrom
06-27-add_posthog_capture_for_optimistic_actions

Conversation

@ahmetskilinc
Copy link
Contributor

@ahmetskilinc ahmetskilinc commented Jun 27, 2025

Add PostHog analytics tracking for email actions

Description

Added PostHog event tracking to various email actions to improve analytics and user behavior insights. Events are now captured when users:

  • Mark emails as read/unread
  • Star/unstar emails
  • Move emails between folders
  • Delete emails
  • Mark emails as important/not important
  • Undo actions

Summary by CodeRabbit

  • New Features
    • Added event tracking for key email actions, including marking as read/unread, starring/un-starring, moving, deleting, marking as important/unimportant, and undoing actions. This improves visibility into user interactions.

Copy link
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 27, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This update refactors the PendingAction type into a discriminated union with strongly typed parameters per action type and introduces an ActionType enum with a mapping to PostHog event name generators. The useOptimisticActions hook now captures analytics events after optimistic actions succeed, using the typed action keys and parameters.

Changes

File(s) Change Summary
apps/mail/hooks/use-optimistic-actions.ts Added ActionType enum and event name mapping; updated createPendingAction to use typed action keys and capture PostHog events after successful optimistic actions.
apps/mail/lib/optimistic-actions-manager.ts Refactored PendingAction type into a discriminated union with specific parameter types per action; added BasePendingAction type and imported ThreadDestination.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant useOptimisticActions
    participant PostHog

    User->>useOptimisticActions: Trigger optimistic action (e.g., MOVE, STAR)
    useOptimisticActions->>useOptimisticActions: Execute optimistic update
    useOptimisticActions->>PostHog: capture(eventName generated from action type and params)
    useOptimisticActions-->>User: Confirm action completion
Loading

Possibly related PRs

  • optimistic thread state for thread context menu #1300: Implements optimistic UI updates for label toggling using the optimistic actions system, extending the typed action framework introduced here.
  • hotfix #1207: Modifies the async handling order in doAction within createPendingAction, related by changes to the same hook but focusing on async flow rather than analytics or typing.

Suggested reviewers

  • MrgSub

Poem

🐇✨
A hop, a skip, an action made,
With typed enums, no chance to fade.
PostHog listens, logs the cheer,
Each move and star now crystal clear.
Optimistic steps in line,
Analytics dance, all in time!
🥕📬


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b43d59 and 4a4a687.

📒 Files selected for processing (2)
  • apps/mail/hooks/use-optimistic-actions.ts (5 hunks)
  • apps/mail/lib/optimistic-actions-manager.ts (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ahmetskilinc ahmetskilinc marked this pull request as ready for review June 27, 2025 17:12
@MrgSub
Copy link
Collaborator

MrgSub commented Jun 27, 2025

This can just spam our event logs - we're not tracking complete actions?

@ahmetskilinc
Copy link
Contributor Author

let me update.

@ahmetskilinc ahmetskilinc force-pushed the 06-27-add_posthog_capture_for_optimistic_actions branch from 2f053c8 to 7b43d59 Compare June 27, 2025 19:08
@ahmetskilinc ahmetskilinc force-pushed the 06-27-add_posthog_capture_for_optimistic_actions branch from 7b43d59 to 4a4a687 Compare July 1, 2025 21:20
Copy link
Contributor Author

ahmetskilinc commented Jul 1, 2025

Merge activity

  • Jul 1, 9:20 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 1, 9:21 PM UTC: @ahmetskilinc merged this pull request with Graphite.

@ahmetskilinc ahmetskilinc merged commit 34fa41e into staging Jul 1, 2025
5 of 6 checks passed
@ahmetskilinc ahmetskilinc deleted the 06-27-add_posthog_capture_for_optimistic_actions branch July 1, 2025 21:21
Comment on lines +25 to +32
const actionEventNames: Record<ActionType, (params: any) => string> = {
[ActionType.MOVE]: () => 'email_moved',
[ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'),
[ActionType.READ]: (params) => (params.read ? 'email_marked_read' : 'email_marked_unread'),
[ActionType.IMPORTANT]: (params) =>
params.important ? 'email_marked_important' : 'email_unmarked_important',
[ActionType.LABEL]: (params) => (params.add ? 'email_label_added' : 'email_label_removed'),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

The params parameter in actionEventNames functions is typed as any, which bypasses TypeScript's type checking. Since the PR has already defined specific parameter types in the PendingAction type union, these could be leveraged here for better type safety.

Consider refactoring to use the appropriate parameter types for each action type:

const actionEventNames: {
  [K in ActionType]: (params: Extract<PendingAction, { type: K }>['params']) => string
} = {
  [ActionType.MOVE]: (params) => 'email_moved',
  [ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'),
  // etc.
};

This approach would provide proper type checking and autocompletion for each action type's parameters.

Suggested change
const actionEventNames: Record<ActionType, (params: any) => string> = {
[ActionType.MOVE]: () => 'email_moved',
[ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'),
[ActionType.READ]: (params) => (params.read ? 'email_marked_read' : 'email_marked_unread'),
[ActionType.IMPORTANT]: (params) =>
params.important ? 'email_marked_important' : 'email_unmarked_important',
[ActionType.LABEL]: (params) => (params.add ? 'email_label_added' : 'email_label_removed'),
};
const actionEventNames: {
[K in ActionType]: (params: Extract<PendingAction, { type: K }>['params']) => string
} = {
[ActionType.MOVE]: (params) => 'email_moved',
[ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'),
[ActionType.READ]: (params) => (params.read ? 'email_marked_read' : 'email_marked_unread'),
[ActionType.IMPORTANT]: (params) =>
params.important ? 'email_marked_important' : 'email_unmarked_important',
[ActionType.LABEL]: (params) => (params.add ? 'email_label_added' : 'email_label_removed'),
};

Spotted by Diamond (based on custom rules)

Is this helpful? React 👍 or 👎 to let us know.

};

optimisticActionsManager.pendingActions.set(pendingActionId, pendingAction);
optimisticActionsManager.pendingActions.set(pendingActionId, pendingAction as PendingAction);
Copy link
Contributor

Choose a reason for hiding this comment

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

The type assertion as PendingAction appears unnecessary here. Consider defining pendingAction with an explicit type annotation instead:

const pendingAction: PendingAction = {
  id: pendingActionId,
  type,
  threadIds,
  params,
  optimisticId,
  execute,
  undo,
};

This would allow TypeScript to validate the object structure at compile time rather than forcing the type with an assertion, which bypasses type checking and could hide potential errors.

Spotted by Diamond (based on custom rules)

Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +25 to +32
const actionEventNames: Record<ActionType, (params: any) => string> = {
[ActionType.MOVE]: () => 'email_moved',
[ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'),
[ActionType.READ]: (params) => (params.read ? 'email_marked_read' : 'email_marked_unread'),
[ActionType.IMPORTANT]: (params) =>
params.important ? 'email_marked_important' : 'email_unmarked_important',
[ActionType.LABEL]: (params) => (params.add ? 'email_label_added' : 'email_label_removed'),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

There appears to be a type inconsistency in how ActionType is defined and used. The actionEventNames object is typed as Record<ActionType, (params: any) => string> where ActionType is an enum (numeric values), but later it's accessed with actionEventNames[type] where type is typed as keyof typeof ActionType (string keys).

To resolve this inconsistency, consider one of these approaches:

  1. Change the type definition to Record<keyof typeof ActionType, (params: any) => string> to match how it's being accessed
  2. Or modify the access pattern to actionEventNames[ActionType[type as keyof typeof ActionType]]

This will ensure type safety and prevent potential runtime errors when accessing event names.

Suggested change
const actionEventNames: Record<ActionType, (params: any) => string> = {
[ActionType.MOVE]: () => 'email_moved',
[ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'),
[ActionType.READ]: (params) => (params.read ? 'email_marked_read' : 'email_marked_unread'),
[ActionType.IMPORTANT]: (params) =>
params.important ? 'email_marked_important' : 'email_unmarked_important',
[ActionType.LABEL]: (params) => (params.add ? 'email_label_added' : 'email_label_removed'),
};
const actionEventNames: Record<keyof typeof ActionType, (params: any) => string> = {
[ActionType.MOVE]: () => 'email_moved',
[ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'),
[ActionType.READ]: (params) => (params.read ? 'email_marked_read' : 'email_marked_unread'),
[ActionType.IMPORTANT]: (params) =>
params.important ? 'email_marked_important' : 'email_unmarked_important',
[ActionType.LABEL]: (params) => (params.add ? 'email_label_added' : 'email_label_removed'),
};

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

@coderabbitai coderabbitai bot mentioned this pull request Aug 11, 2025
3 tasks
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