@osobh/reactar
Version:
AR Library for React Native and Expo
131 lines (111 loc) • 3.51 kB
text/typescript
// src/core/UserSessionManager.ts
import { EventEmitter } from 'events';
export interface ARUser {
userId: string;
deviceId: string;
position: { x: number; y: number; z: number };
rotation: { x: number; y: number; z: number };
lastUpdate: number;
isActive: boolean;
}
export interface UserSessionOptions {
serverUrl?: string;
heartbeatInterval?: number;
inactivityTimeout?: number;
}
export class UserSessionManager extends EventEmitter {
private options: UserSessionOptions;
private users: Map<string, ARUser>;
private currentUser: ARUser | null = null;
private heartbeatInterval: NodeJS.Timeout | null = null;
constructor(options: UserSessionOptions = {}) {
super();
this.options = {
serverUrl: options.serverUrl || 'https://ar-session-service.example.com',
heartbeatInterval: options.heartbeatInterval || 1000,
inactivityTimeout: options.inactivityTimeout || 5000,
};
this.users = new Map();
}
/**
* Initializes a user session
*/
public async initializeSession(deviceId: string): Promise<ARUser> {
try {
const userId = `user-${Math.random().toString(36).substr(2, 9)}`;
this.currentUser = {
userId,
deviceId,
position: { x: 0, y: 0, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
lastUpdate: Date.now(),
isActive: true,
};
this.users.set(userId, this.currentUser);
this.emit('userJoined', this.currentUser);
this.startHeartbeat();
return this.currentUser;
} catch (error) {
this.emit('error', error);
throw error;
}
}
/**
* Updates the current user's position and rotation
*/
public updateUserPosition(position: { x: number; y: number; z: number }, rotation: { x: number; y: number; z: number }): void {
if (!this.currentUser) {
throw new Error('No active user session');
}
this.currentUser.position = position;
this.currentUser.rotation = rotation;
this.currentUser.lastUpdate = Date.now();
this.users.set(this.currentUser.userId, this.currentUser);
this.emit('userUpdated', this.currentUser);
}
/**
* Handles incoming user updates from other devices
*/
public handleRemoteUserUpdate(user: ARUser): void {
if (user.userId === this.currentUser?.userId) return;
const existingUser = this.users.get(user.userId);
if (existingUser) {
Object.assign(existingUser, user);
} else {
this.users.set(user.userId, user);
this.emit('userJoined', user);
}
this.emit('userUpdated', user);
}
/**
* Gets all active users in the session
*/
public getActiveUsers(): ARUser[] {
const now = Date.now();
return Array.from(this.users.values()).filter(
user => user.isActive && now - user.lastUpdate < this.options.inactivityTimeout!
);
}
private startHeartbeat(): void {
if (this.heartbeatInterval) return;
this.heartbeatInterval = setInterval(() => {
if (this.currentUser) {
this.currentUser.lastUpdate = Date.now();
this.emit('heartbeat', this.currentUser);
}
}, this.options.heartbeatInterval);
}
/**
* Ends the current user's session
*/
public endSession(): void {
if (!this.currentUser) return;
this.currentUser.isActive = false;
this.emit('userLeft', this.currentUser);
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
}
this.currentUser = null;
}
}