Skip to main content

Mark Notification Tray Item Seen

Fine-grained Tracking

Update seen status for individual notification items

User Interaction

Track when users click or interact with specific notifications

Overview

The notification tray item markSeen() method updates the seen status of a specific notification tray item, enabling fine-grained read tracking. Use this when a user clicks or interacts with an individual notification. Each tray item has its own lastSeenAt timestamp, which is recorded on the server when this method is called.

Implementation Guide

import AmitySDK

class NotificationItemCell: UITableViewCell {
    var notificationItem: AmityNotificationTrayItem?
    
    @IBAction func handleItemTap(_ sender: UITapGestureRecognizer) {
        guard let item = notificationItem else { return }
        
        // Mark individual item as seen
        item.markSeen { [weak self] result in
            DispatchQueue.main.async {
                switch result {
                case .success:
                    self?.updateSeenUI()
                case .failure(let error):
                    self?.handleError(error)
                }
            }
        }
        
        // Navigate to relevant content
        navigateToContent(item)
    }
    
    private func updateSeenUI() {
        // Update visual indicators
        seenIndicator.isHidden = true
        backgroundColor = UIColor.systemBackground
    }
}

Usage Patterns

User Interaction

Primary Trigger
  • User taps/clicks on notification item
  • User navigates to referenced content
  • User explicitly dismisses notification

View Tracking

Secondary Triggers
  • Item appears in viewport for extended time
  • User scrolls past item slowly
  • Item receives focus in keyboard navigation
Track notification engagement patterns to optimize user experience:
class NotificationAnalytics {
    static func trackNotificationInteraction(_ item: AmityNotificationTrayItem) {
        let properties: [String: Any] = [
            "notification_id": item.notificationId,
            "action_type": item.actionType,
            "target_type": item.targetType,
            "actor_count": item.actorCount,
            "time_to_interaction": calculateTimeToInteraction(item),
            "was_seen_before": item.isSeen
        ]
        
        Analytics.track("notification_item_interacted", properties: properties)
    }
    
    private static func calculateTimeToInteraction(_ item: AmityNotificationTrayItem) -> TimeInterval {
        return Date().timeIntervalSince(item.lastOccuredAt)
    }
}

Visual Feedback Patterns

Unread Indicators

  • Colored dots or badges
  • Different background colors
  • Bold text styling

Loading States

  • Spinner during API call
  • Disabled interaction
  • Progress indicators

Seen Confirmation

  • Immediate visual update
  • Smooth animations
  • Clear visual hierarchy

CSS Implementation Example

.notification-item {
    padding: 16px;
    border-bottom: 1px solid #e0e0e0;
    transition: all 0.3s ease;
    cursor: pointer;
}

.notification-item.unseen {
    background-color: #f3f4f6;
    border-left: 4px solid #3b82f6;
}

.notification-item.seen {
    background-color: white;
    border-left: 4px solid transparent;
}

.notification-item.marking {
    opacity: 0.7;
    pointer-events: none;
}

.seen-indicator {
    width: 8px;
    height: 8px;
    background-color: #3b82f6;
    border-radius: 50%;
    margin-left: auto;
}

.seen-indicator.hidden {
    display: none;
}

Error Handling

Network Issues

Handle offline scenarios and connectivity problems

Item State Conflicts

Manage cases where item state changes during API call

Permission Errors

Handle authentication and authorization failures

Server Errors

Manage server-side processing issues
class NotificationItemErrorHandler {
    func markItemSeenWithErrorHandling(_ item: AmityNotificationTrayItem, completion: @escaping (Bool) -> Void) {
        item.markSeen { result in
            DispatchQueue.main.async {
                switch result {
                case .success:
                    completion(true)
                    
                case .failure(let error):
                    self.handleMarkSeenError(error, item: item, completion: completion)
                }
            }
        }
    }
    
    private func handleMarkSeenError(_ error: Error, item: AmityNotificationTrayItem, completion: @escaping (Bool) -> Void) {
        if let amityError = error as? AmityError {
            switch amityError.code {
            case .networkError:
                showRetryOption(for: item, completion: completion)
            case .permissionDenied:
                showPermissionError()
                completion(false)
            case .itemNotFound:
                // Item might have been deleted, treat as success
                completion(true)
            default:
                showGenericError()
                completion(false)
            }
        } else {
            showGenericError()
            completion(false)
        }
    }
    
    private func showRetryOption(for item: AmityNotificationTrayItem, completion: @escaping (Bool) -> Void) {
        let alert = UIAlertController(
            title: "Connection Error",
            message: "Failed to mark notification as seen. Would you like to retry?",
            preferredStyle: .alert
        )
        
        alert.addAction(UIAlertAction(title: "Retry", style: .default) { _ in
            self.markItemSeenWithErrorHandling(item, completion: completion)
        })
        
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
            completion(false)
        })
        
        // Present alert
        if let topViewController = UIApplication.shared.topViewController {
            topViewController.present(alert, animated: true)
        }
    }
}

Best Practices

Timing

When to Call
  • User clicks/taps notification item
  • User navigates to referenced content
  • User explicitly dismisses notification
  • Item is viewed for significant duration

UX Considerations

User Experience
  • Provide immediate visual feedback
  • Handle loading states gracefully
  • Implement retry mechanisms for failures
  • Maintain consistent visual hierarchy
  • Batch Operations: Consider batching multiple mark-seen calls when possible
  • Debounce Rapid Calls: Prevent duplicate API calls for the same item
  • Local State Updates: Update UI immediately, sync with server asynchronously
  • Error Recovery: Implement exponential backoff for failed requests
  • Clear Visual Hierarchy: Distinguish between seen and unseen items clearly
  • Smooth Transitions: Use animations for state changes
  • Loading Indicators: Show progress for mark-seen operations
  • Accessibility: Ensure seen/unseen states are accessible to screen readers
  • State Management: Keep local UI state in sync with server state
  • Error Handling: Provide meaningful error messages and recovery options
  • Analytics: Track interaction patterns for product insights
  • Testing: Test offline scenarios and edge cases thoroughly