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
- Cache participant data - Don't call
getAll()on every render - Use colors for visual cues - Each user has a unique color
- Respect permissions - Always check
me.rolebefore showing admin UI - Handle offline users - Filter by
isOnlinewhen needed - Unsubscribe on cleanup - Prevent memory leaks
What's Next?
- Storage API - Sync component state
- Lifecycle Events - Handle component events
- Props API - Read component configuration