Use case: Track when users view annotation replies
Before you start
Before you track read receipts for annotations, ensure you have completed the following prerequisites:
- Project Access: Ensure you have the necessary permissions to access and update annotations in the target project.
- Understand Read Receipts: Read receipts track when a user last viewed the replies to an annotation thread, enabling "unread" indicators in your UI.
- Time Synchronization: Ensure your system time is accurate, as timestamps are used to determine which replies are unread.
- Set Up SDK Environment: Confirm that your Unity environment is correctly configured with the Collaboration SDK.
- User Context: The read receipt is associated with the authenticated user making the request.
How do I...?
Record when a user views annotation replies
To track that a user has viewed an annotation's replies at a specific time, follow these steps:
- Prepare the Project Identifier: Ensure you have the correct project identifier (GUID) for the project containing the annotation.
- Prepare the Annotation Identifier: Ensure you have the correct annotation identifier (GUID) for the annotation thread.
- Capture the Timestamp: Record the exact time when the user viewed the replies. Use the current time if tracking a real-time view.
- Upsert the Read Receipt: Use the SDK to record the read receipt timestamp for the annotation.
- Update UI: Use this timestamp to determine which replies are "new" or "unread" in your user interface.
Example:
var projectId = new ProjectId("87cf845f-8ca7-4b2f-9cc1-c3d731335810");
var annotationId = new AnnotationId("a1b2c3d4-e5f6-7890-abcd-ef1234567890");
var annotationReference = new annotationReference(projectId, annotationId);
// Record the current time as when the user viewed the replies
var timestamp = DateTime.UtcNow;
await annotationManagement.UpsertReadReceiptAsync(
annotationReference,
timestamp,
cancellationToken);
Debug.Log($"Read receipt recorded for annotation {annotationId} at {timestamp}");
Mark an annotation thread as read when user opens it
When a user opens an annotation thread in your UI, record the read receipt:
public async Task OnAnnotationThreadOpened(ProjectId projectId, AnnotationId annotationId)
{
try
{
// Record that the user has now seen all replies up to this moment
await annotationManagement.UpsertReadReceiptAsync(
annotationReference,
DateTime.UtcNow,
cancellationToken);
// Update your UI to reflect that all replies are now "read"
UpdateUIReadStatus(annotationId, allRead: true);
}
catch (Exception ex)
{
Debug.LogError($"Failed to update read receipt: {ex.Message}");
}
}
Determine which replies are unread
Combine read receipts with annotation data to identify unread replies:
// First, get the annotation with reply information
var annotationResult = await annotationManagement.ReadAnnotationAsync(
annotationReference,
cancellationToken: cancellationToken);
var annotation = annotationResult.Annotation;
// Check if there are replies newer than the user's last read timestamp
if (annotation.ReplyLastReadTimestamp.HasValue && annotation.LatestReply.HasValue)
{
if (annotation.LatestReply.Value > annotation.ReplyLastReadTimestamp.Value)
{
Debug.Log("This annotation has unread replies!");
// Show unread indicator in UI
int unreadCount = annotation.ReplyUnreadCount ?? 0;
Debug.Log($"Unread replies count: {unreadCount}");
}
else
{
Debug.Log("All replies have been read.");
}
}
// When user views the thread, update the read receipt
await annotationManagement.UpsertReadReceiptAsync(
annotationReference,
DateTime.UtcNow,
cancellationToken);
Record read receipt with specific historical timestamp
If you need to record that replies were read at a specific point in the past:
var projectId = new ProjectId("87cf845f-8ca7-4b2f-9cc1-c3d731335810");
var annotationId = new AnnotationId("a1b2c3d4-e5f6-7890-abcd-ef1234567890");
var annotationReference = new annotationReference(projectId, annotationId);
// Record that the user viewed replies at a specific historical time
var historicalTimestamp = new DateTime(2024, 3, 15, 14, 30, 0, DateTimeKind.Utc);
await annotationManagement.UpsertReadReceiptAsync(
annotationReference,
historicalTimestamp,
cancellationToken);
Understanding read receipts
Read receipts enable "unread" functionality in annotation threads:
- Per-User Tracking: Each user has their own read receipt timestamp for each annotation thread
- Upsert Operation: Calling this method creates a new read receipt if none exists, or updates the existing one
- UTC Timestamps: Always use UTC timestamps for consistency across time zones
- Automatic Calculation: The server automatically calculates unread counts based on reply timestamps versus read receipt timestamps
- UI Integration: Use the
ReplyLastReadTimestampandReplyUnreadCountproperties from annotation data to build unread indicators
Best practices
- Record on View: Update read receipts when users actually view the thread, not when they receive notifications
- Use Current Time: In most cases, use
DateTime.UtcNowwhen recording real-time views - Handle Offline: Queue read receipt updates when offline and sync when connection is restored
- Batch Updates: If updating multiple read receipts, consider batching the operations
- Error Handling: Handle errors gracefully as read receipt updates should not block the user experience
For related annotation operations, see documentation on finding replies to an annotation and reading annotations.
See the API documentation for more details on the UpsertReadReceiptAsync method.