Skip to content

board.participants - Users & Permissions

Access information about users on the board and track join/leave events.

Overview

board.participants provides information about:

  • Current user (me)
  • All participants on the board
  • Join/leave events
  • User roles and permissions

Current User

me - Get Current User

typescript
const me = board.participants.me;

console.log(me);
/*
{
  id: 'user-123',
  name: 'Alice Smith',
  avatarUrl: 'https://cdn.example.com/avatar.jpg',
  role: 'host',  // 'host' | 'guest'
  color: '#3B82F6'
}
*/

Check Permissions

typescript
// Check if current user is host
const isHost = board.participants.me.role === 'host';

if (isHost) {
  // Show admin controls
  showAdminPanel();
}

// Check if user can edit
const canEdit = board.participants.me.role === 'host';

All Participants

getAll() - List All Users

typescript
const participants = board.participants.getAll();

console.log(participants);
/*
[
  {
    id: 'user-123',
    name: 'Alice Smith',
    avatarUrl: 'https://...',
    role: 'host',
    color: '#3B82F6',
    isOnline: true
  },
  {
    id: 'user-456',
    name: 'Bob Johnson',
    avatarUrl: 'https://...',
    role: 'guest',
    color: '#EF4444',
    isOnline: true
  }
]
*/

Events

subscribe() - Join/Leave Events

typescript
// User joined
board.participants.subscribe('join', (participant) => {
  console.log(`${participant.name} joined the board`);
  showNotification(`${participant.name} joined`);
});

// User left
board.participants.subscribe('leave', (participant) => {
  console.log(`${participant.name} left the board`);
  showNotification(`${participant.name} left`);
});

// User updated (changed name, avatar, etc.)
board.participants.subscribe('update', (participant) => {
  console.log(`${participant.name} updated their profile`);
  updateParticipantUI(participant.id);
});

Examples

Participant Counter

typescript
import { BoardAPI } from '@boardapi/sdk';

const board = new BoardAPI();

// Update participant count
function updateCount() {
  const participants = board.participants.getAll();
  const count = participants.filter(p => p.isOnline).length;

  document.querySelector('#participant-count').textContent = count.toString();
}

// Initial count
board.on('ready', () => {
  updateCount();
});

// Update on join/leave
board.participants.subscribe('join', updateCount);
board.participants.subscribe('leave', updateCount);

Avatar List

typescript
function renderAvatars() {
  const participants = board.participants.getAll();
  const container = document.querySelector('#avatars');

  container.innerHTML = participants
    .filter(p => p.isOnline)
    .map(p => `
      <div class="avatar" title="${p.name}">
        <img src="${p.avatarUrl}" alt="${p.name}">
        <div class="badge" style="background-color: ${p.color}"></div>
      </div>
    `)
    .join('');
}

board.on('ready', renderAvatars);
board.participants.subscribe('join', renderAvatars);
board.participants.subscribe('leave', renderAvatars);
board.participants.subscribe('update', renderAvatars);

Role-Based UI

typescript
const me = board.participants.me;

// Show different UI based on role
if (me.role === 'host') {
  // Teacher/admin controls
  document.querySelector('#admin-panel').style.display = 'block';
  document.querySelector('#reveal-button').style.display = 'block';
} else {
  // Student/guest controls
  document.querySelector('#vote-panel').style.display = 'block';
}

Collaborative Editing Indicator

typescript
interface EditingState {
  userId: string;
  field: string;
}

// Track who's editing what
const currentlyEditing = new Map<string, EditingState>();

// Mark field as being edited
function startEditing(field: string) {
  const state: EditingState = {
    userId: board.participants.me.id,
    field
  };

  board.storage.setTransient(`editing:${field}`, state);
  showEditingIndicator(field, board.participants.me);
}

// Clear editing state
function stopEditing(field: string) {
  board.storage.delete(`editing:${field}`);
  hideEditingIndicator(field);
}

// Listen for others editing
board.storage.subscribeAll((changes) => {
  changes.forEach(change => {
    if (change.key.startsWith('editing:')) {
      const field = change.key.replace('editing:', '');
      const state = change.newValue as EditingState;

      if (state && state.userId !== board.participants.me.id) {
        const participant = board.participants.getAll()
          .find(p => p.id === state.userId);

        if (participant) {
          showEditingIndicator(field, participant);
        }
      }
    }
  });
});

Last Active Tracker

typescript
interface UserActivity {
  userId: string;
  lastActive: string;
  action: string;
}

// Track activity
async function trackActivity(action: string) {
  const activity: UserActivity = {
    userId: board.participants.me.id,
    lastActive: new Date().toISOString(),
    action
  };

  await board.storage.set(`activity:${board.participants.me.id}`, activity);
}

// Show who's active
function showActiveUsers() {
  const participants = board.participants.getAll();
  const now = Date.now();

  participants.forEach(p => {
    const activity = board.storage.get<UserActivity>(`activity:${p.id}`);

    if (activity) {
      const lastActiveMs = new Date(activity.lastActive).getTime();
      const minutesAgo = Math.floor((now - lastActiveMs) / 60000);

      if (minutesAgo < 5) {
        markAsActive(p.id, activity.action);
      }
    }
  });
}

Participant Object Reference

typescript
interface Participant {
  id: string;           // Unique user ID
  name: string;         // Display name
  avatarUrl: string;    // Avatar image URL
  role: 'host' | 'guest';  // User role
  color: string;        // Unique color (hex)
  isOnline: boolean;    // Online status
}

Best Practices

  1. Cache participant data - Don't call getAll() on every render
  2. Use colors for visual cues - Each user has a unique color
  3. Respect permissions - Always check me.role before showing admin UI
  4. Handle offline users - Filter by isOnline when needed
  5. Unsubscribe on cleanup - Prevent memory leaks

What's Next?