Hone logo
Hone
Problems

Real-time Collaborative Text Editor

Imagine building a feature for a document editing application where multiple users can edit the same document simultaneously, seeing each other's changes in real-time. This challenge focuses on implementing the core logic for such a collaborative text editor in React using TypeScript.

Problem Description

You need to create a React component that simulates a collaborative text editor. This component will manage a shared document state and allow multiple "cursors" (representing different users) to interact with it. The primary goal is to ensure that all changes made by any user are reflected accurately and immediately for all other users viewing the document.

Key Requirements:

  1. Shared Document State: Maintain a single source of truth for the document's content.
  2. Real-time Updates: Changes made by one user (insertions, deletions) should be broadcast and applied to the document state for all other connected users.
  3. Cursor Presence: Display the cursors of other users, showing their current position within the document.
  4. Cursor Movement: When a user moves their cursor, this movement should be reflected for other users.
  5. Idempotent Operations: Ensure that applying the same operation multiple times doesn't lead to incorrect state.
  6. Conflict Resolution (Simplified): For this challenge, assume a simplified conflict resolution where the last operation received wins. More advanced conflict resolution (like Operational Transformation or CRDTs) is out of scope but could be a future extension.

Expected Behavior:

  • When User A types a character, User B should see that character appear at the correct position in their editor.
  • When User A deletes a character, User B should see that character disappear.
  • When User A moves their cursor to a new position, User B should see User A's cursor jump to that new position.
  • The component should render the document content and the cursors of all active users.

Edge Cases:

  • Simultaneous typing at the same position (handled by simplified conflict resolution: last write wins).
  • Rapid cursor movements.
  • Empty document.
  • Users joining and leaving the "session" (for simulation purposes, you'll simulate multiple users within a single component instance).

Examples

Let's consider a simplified simulation where we manage the state for a single document and multiple "users" (represented by different IDs). We'll use a basic string for the document content.

Example 1: Text Insertion

Initial State:

  • Document: "Hello"
  • User 1 Cursor: Position 5 (at the end of "Hello")
  • User 2 Cursor: Position 0 (at the beginning)

User 1 Action: Inserts " world!" at position 5.

  • Simulated Event: {"type": "insert", "position": 5, "text": " world!"}
  • Simulated User ID: "user1"

Expected Document State After User 1 Action: "Hello world!"

User 2 Action: Inserts "Goodbye " at position 0.

  • Simulated Event: {"type": "insert", "position": 0, "text": "Goodbye "}
  • Simulated User ID: "user2"

Expected Document State After User 2 Action: "Goodbye Hello world!" (assuming User 2's operation is processed after User 1's in this simulation)

Example 2: Text Deletion

Initial State:

  • Document: "abcdef"
  • User 1 Cursor: Position 3
  • User 2 Cursor: Position 1

User 1 Action: Deletes 1 character at position 3 (deletes 'd').

  • Simulated Event: {"type": "delete", "position": 3, "length": 1}
  • Simulated User ID: "user1"

Expected Document State After User 1 Action: "abcef"

User 2 Action: Deletes 1 character at position 0 (deletes 'a').

  • Simulated Event: {"type": "delete", "position": 0, "length": 1}
  • Simulated User ID: "user2"

Expected Document State After User 2 Action: "bcef" (assuming User 2's operation is processed after User 1's)

Example 3: Cursor Movement

Initial State:

  • Document: "Example text"
  • User 1 Cursor: Position 0
  • User 2 Cursor: Position 5

User 1 Action: Moves cursor to position 7.

  • Simulated Event: {"type": "cursor_move", "position": 7}
  • Simulated User ID: "user1"

Expected Cursor State for User 2: User 1's cursor should now be at position 7.

Constraints

  • The document content will be a string.
  • User IDs will be unique strings.
  • Cursor positions will be non-negative integers within the bounds of the document length.
  • Operations (insert, delete, cursor_move) will be sent as plain JavaScript objects.
  • For this challenge, you will simulate network latency and multiple users by having a single React component that manages an array of "user states" and processes incoming "events" sequentially. You do not need to implement actual networking (e.g., WebSockets).

Notes

  • You will likely need to manage a state for the document content and a separate state for each user's cursor position.
  • Consider how you will represent different users and their cursors.
  • Think about the order in which operations are applied. For insertions and deletions, the order matters significantly.
  • A good starting point would be to define interfaces for your event types and user states.
  • The react-use-websocket or similar libraries are often used for real-world implementations, but for this challenge, focus on the state management and logic within React.
Loading editor...
typescript