Skip to content

Conversation

@sgrimee
Copy link
Contributor

@sgrimee sgrimee commented Oct 26, 2025

Add unified message read status tracking and control

Summary

This PR adds comprehensive message read status tracking and control to webex-rust, unifying features from two separate branches into a cohesive implementation.

Features Added

1. Message Read/Unread Control

  • mark_message_as_read(message_id, room_id) - Mark messages as read on server
  • mark_message_as_unread(message_id, room_id) - Mark messages as unread for later review
  • ✅ Changes sync across all Webex clients in real-time

2. Read Status Tracking

  • get_room_with_read_status(room_id) - Get room with read status info
  • list_rooms_with_read_status() - List all rooms with read status
  • ReadStatus struct for tracking last seen messages and unread state

3. Membership API Support

  • Membership type now implements Gettable trait
  • get_room_memberships(room_id) - Get all members in a room
  • get_person_memberships(person_id) - Get all rooms for a person
  • ✅ Works with generic API: list<Membership>(), get<Membership>(id)

4. WebSocket Event Support

  • MembershipActivity enum for membership events
  • ✅ Support for memberships:seen events (read receipts)
  • ✅ Event types: Seen, Created, Updated, Deleted

API Integration

The new API complements the existing API perfectly with zero duplication:

Integration Aspect Status
Duplicated methods ✅ 0
Conflicting methods ✅ 0
Breaking changes ✅ 0
Follows existing patterns ✅ Yes
Extends generic API naturally ✅ Yes

How It Integrates

Generic API Extension (follows existing pattern):

// Already worked for Message, Room, Person, etc.
let messages: Vec<Message> = webex.list().await?;

// Now works for Membership too (same pattern):
let memberships: Vec<Membership> = webex.list().await?;

Convenience Methods (mirrors existing pattern):

// Existing convenience method:
let all_rooms = webex.get_all_rooms().await?;

// New convenience methods (same pattern):
let members = webex.get_room_memberships(room_id).await?;
let my_rooms = webex.get_person_memberships(person_id).await?;

Enhancement Pattern (doesn't replace existing):

// Original room API still works:
let room: Room = webex.get(&room_id).await?;

// Enhanced version adds read status:
let room_with_status = webex.get_room_with_read_status(&room_id).await?;

New Capabilities (orthogonal functionality):

// Completely new - no overlap with existing CRUD operations:
webex.mark_message_as_read(message_id, room_id).await?;
webex.mark_message_as_unread(message_id, room_id).await?;

Implementation Details

Data Structures

Unified Membership struct:

pub struct Membership {
    pub id: String,
    pub room_id: String,
    pub person_id: String,
    pub person_email: String,
    pub person_display_name: String,
    pub person_org_id: String,
    pub is_moderator: bool,
    pub is_room_hidden: Option<bool>,
    pub room_type: Option<String>,
    pub is_monitor: Option<bool>,
    pub last_seen_id: Option<String>,  // For read status
    pub created: String,
}

ReadStatus tracking:

pub struct ReadStatus {
    pub last_seen_id: Option<String>,
    pub last_seen_date: Option<String>,
    pub last_activity_id: Option<String>,
    pub has_unread: bool,
}

MembershipActivity events:

pub enum MembershipActivity {
    Seen,      // Read receipt (memberships:seen event)
    Created,   // User added to room
    Updated,   // Membership updated
    Deleted,   // User removed from room
}

How Mark as Read/Unread Works

Mark as Read:

  1. Gets user's membership in the room
  2. Updates lastSeenId field to the target message ID
  3. Syncs via PUT /v1/memberships/{membershipId}

Mark as Unread:

  1. Finds the message before the target message
  2. Sets lastSeenId to that previous message ID
  3. Target message and all after it appear as unread
  4. Syncs across all Webex clients

Examples

Example 1: Interactive Read/Unread Bot

examples/mark-as-read.rs - Bot that responds to commands:

  • Send "mark as read" → marks message as read
  • Send "set to unread" → marks message as unread
  • Other text → shows help

Example 2: Read Status Tracking

examples/read-status.rs - Demonstrates:

  • Listing rooms with read status
  • Getting membership information
  • Listening for membership:seen WebSocket events

Documentation

  • MESSAGE_READ_STATUS.md - Comprehensive feature documentation
  • ✅ Updated README.md with new capabilities
  • ✅ Inline code documentation for all public APIs
  • ✅ Usage examples in doc comments

Testing

To test:

# Interactive mark as read/unread
BOT_ACCESS_TOKEN="token" BOT_EMAIL="bot@webex.bot" cargo run --example mark-as-read

# Read status tracking and events
BOT_ACCESS_TOKEN="token" cargo run --example read-status

Fulfills README Promise

The original README claimed:

Current functionality includes:

  • Getting room memberships

But this was never implemented. This PR delivers on that promise and adds bonus read/unread functionality.

Breaking Changes

None. All existing APIs continue to work unchanged.

Commits

  1. 362652d - Add feature to mark messages as read on the server
  2. 8031fc7 - Add capability to mark messages as unread
  3. 5daa5a5 - Unify message read status features from both branches

Related Work

This PR unifies and supersedes:

  • Branch claude/webex-message-status-011CULyyyEDqD3obksGqKZ4s (read status tracking)
  • Current branch features (mark as read/unread)

Files Changed

  • src/types.rs - Added Membership, ReadStatus, RoomWithReadStatus structs; MembershipActivity enum
  • src/lib.rs - Added 6 new public methods for memberships and read status
  • README.md - Updated feature list
  • MESSAGE_READ_STATUS.md - Comprehensive documentation
  • examples/mark-as-read.rs - Interactive mark as read/unread example
  • examples/read-status.rs - Read status tracking example

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

This commit adds functionality to mark messages as read on the Webex server,
allowing other clients to see the message as read.

Changes:
- Added Membership, MembershipListParams, and MembershipUpdateParams types to types.rs
- Implemented Gettable trait for Membership to enable API operations
- Added mark_message_as_read() method to Webex struct in lib.rs
- Created mark-as-read.rs example demonstrating the new functionality
- Updated README.md and lib.rs documentation to reflect the new feature

The implementation works by:
1. Getting the current user's person ID
2. Finding the user's membership in the specified room
3. Updating the membership's lastSeenId field to the target message ID

This uses the Webex Memberships API endpoint (PUT /v1/memberships/{membershipId})
with the lastSeenId parameter to sync read status across all Webex clients.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit adds the ability to mark messages as unread on the Webex server,
complementing the existing mark-as-read functionality.

Changes:
- Added mark_message_as_unread() method to Webex struct in lib.rs
- Refactored mark_message_as_read() to use shared update_read_status() helper
- Added update_read_status() internal helper method to handle both read/unread
- Updated mark-as-read.rs example to demonstrate both read and unread functionality
- Updated README.md and lib.rs documentation to reflect mark as unread feature

The mark_message_as_unread implementation works by:
1. Finding the message that comes before the target message
2. Setting lastSeenId to that previous message ID
3. If the target is the first message, sets lastSeenId to None to mark all as unread

This allows users to mark messages for later review, with the read status
syncing across all Webex clients.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit creates a unified implementation combining features from:
- claude/mark-message-read-011CULz6JDQ2ufiLFLR5Wh17 (mark as read/unread)
- claude/webex-message-status-011CULyyyEDqD3obksGqKZ4s (read status tracking)

Changes to types.rs:
- Added RoomWithReadStatus struct with ReadStatus for tracking read state
- Added ReadStatus struct with helper methods (from_room, calculate_has_unread, mark_as_read)
- Unified Membership struct to include last_seen_id field and all fields from both branches
- Added MembershipActivity enum (Seen, Created, Updated, Deleted)
- Added MembershipActivity to ActivityType enum
- Implemented TryFrom<&str> for MembershipActivity
- Updated Event::activity_type() to parse MembershipActivity

Changes to lib.rs:
- Added get_room_with_read_status() for reading room with status
- Added list_rooms_with_read_status() for listing all rooms with status
- Added get_room_memberships() for querying room members
- Added get_person_memberships() for querying person's rooms
- Kept mark_message_as_read() for updating read status on server
- Kept mark_message_as_unread() for marking messages for later review
- Kept update_read_status() helper for shared read/unread logic

New files:
- Added MESSAGE_READ_STATUS.md with comprehensive documentation
- Added examples/read-status.rs demonstrating read status tracking and events

This unified implementation provides:
✅ Complete read status tracking (query and display)
✅ Complete read status updates (mark as read/unread)
✅ WebSocket event support for membership:seen events
✅ Consistent Membership structure across all operations
✅ Clear documentation and examples for all features

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This file contains the complete pull request description for creating
the PR on GitHub.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@sgrimee
Copy link
Contributor Author

sgrimee commented Oct 26, 2025

sorry this was premature :-)

@sgrimee sgrimee closed this Oct 26, 2025
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