connect method

void connect({
  1. required String sessionId,
  2. required SocketRole role,
  3. String? clientId,
  4. dynamic onLog(
    1. String
    )?,
  5. dynamic onImageReceived(
    1. Uint8List bytes,
    2. String type,
    3. Map<String, dynamic>? meta
    )?,
  6. dynamic onControllerConnected()?,
  7. dynamic onControllerDisconnected()?,
  8. dynamic onViewerConnected()?,
  9. dynamic onViewerDisconnected()?,
  10. dynamic onPairingSuccess()?,
  11. dynamic onPairingRejected(
    1. String
    )?,
  12. dynamic onSessionEnded()?,
  13. dynamic onActionReceived(
    1. Map<String, dynamic> action
    )?,
  14. dynamic onError(
    1. String
    )?,
  15. dynamic onWarning(
    1. String
    )?,
})

Implementation

void connect({
  required String sessionId,
  required SocketRole role,
  String? clientId,
  Function(String)? onLog,
  Function(Uint8List, String, Map<String, dynamic>?)? onImageReceived,
  Function()? onControllerConnected,
  Function()? onControllerDisconnected,
  Function()? onViewerConnected,
  Function()? onViewerDisconnected,
  Function()? onPairingSuccess,
  Function(String)? onPairingRejected,
  Function()? onSessionEnded,
  Function(Map<String, dynamic>)? onActionReceived,
  Function(String)? onError,
  Function(String)? onWarning,
}) {
  this.onLog = onLog;
  this.onImageReceived = onImageReceived;
  this.onControllerConnected = onControllerConnected;
  this.onControllerDisconnected = onControllerDisconnected;
  this.onViewerConnected = onViewerConnected;
  this.onViewerDisconnected = onViewerDisconnected;
  this.onPairingSuccess = onPairingSuccess;
  this.onPairingRejected = onPairingRejected;
  this.onSessionEnded = onSessionEnded;
  this.onActionReceived = onActionReceived;
  this.onError = onError;
  this.onWarning = onWarning;

  currentSessionId = sessionId;
  currentRole = role;

  // Tear down any previous socket cleanly
  _teardown();

  _socket = IO.io(
    'https://mirror.plushvie.store',
    IO.OptionBuilder()
        .setTransports(['websocket'])
        .disableAutoConnect()
        .enableReconnection()
        .setReconnectionAttempts(5)
        .setReconnectionDelay(1000)
        .build(),
  );

  // ── connect ───────────────────────────────────────────────────────────
  _socket!.onConnect((_) {
    onLog?.call('✅ Connected to server: ${_socket!.id}');

    final payload = <String, dynamic>{
      'sessionId': sessionId,
      'role': role == SocketRole.viewer ? 'viewer' : 'controller',
    };
    if (role == SocketRole.viewer && clientId != null) {
      payload['clientId'] = clientId;
    }

    _socket!.emit('join-tryon-session', payload);
    onLog?.call('📡 Emitted join-tryon-session');

    if (role == SocketRole.viewer) {
      isViewerConnected = true;
      _startHeartbeat();
    }

    // _startPresenceTimer();
  });

  _socket!.onConnectError((data) {
    onLog?.call('❌ Connection error: $data');
    onError?.call(data.toString());
  });

  _socket!.onDisconnect((_) {
    onLog?.call('❌ Disconnected from server');
    _stopHeartbeat();
    _stopPresenceTimer();
    isViewerConnected = false;
  });

  // ── Pairing ───────────────────────────────────────────────────────────
  _socket!.on('pairing-success', (_) {
    onLog?.call('✅ Pairing success!');
    onPairingSuccess?.call();
  });

  _socket!.on('pairing-rejected', (data) {
    final reason = data is Map ? data['reason'] : data.toString();
    onLog?.call('❌ Pairing rejected: $reason');
    onPairingRejected?.call(reason);
  });

  // ── Presence events ───────────────────────────────────────────────────

  // Remote controller joined
  _socket!.on('controller-connected', (_) {
    onLog?.call('📱 Remote controller connected');
    isControllerConnected = true;
    _lastControllerAck = DateTime.now();
    onControllerConnected?.call();
    final ctx = navigatorKeyTry.currentContext;
    if (ctx != null) {
      ctx.read<TryOnDataUiProvider>().sendInitialStateToRemote();
    }
  });

  // Kiosk itself reconnected (server echo)
  _socket!.on('viewer-connected', (_) {
    onLog?.call('🖥 Viewer (kiosk) connected');
    isViewerConnected = true;
    onViewerConnected?.call();
  });

  // Remote controller disconnected
  _socket!.on('controller-disconnected', (_) {
    onLog?.call('📴 Remote controller disconnected');
    _markControllerOffline();
  });

  // Kiosk temporarily lost connection (server tells remote — we also handle it locally)
  _socket!.on('viewer-disconnected-temporary', (_) {
    onLog?.call('⚠️ Viewer temporarily disconnected');
    onViewerDisconnected?.call();
  });

  // ── Data events ───────────────────────────────────────────────────────

  _socket!.on('tryon-viewer-action', (action) {
    onLog?.call('🎮 Remote action received: $action');
    if (action is Map) {
      onActionReceived?.call(Map<String, dynamic>.from(action));
    }
  });

  _socket!.on('tryon-image', (data) => _handleImageReceived(data));

  _socket!.on('tryon-error', (msg) {
    final e = msg.toString();
    onLog?.call('❌ Error: $e');
    onError?.call(e);
  });

  _socket!.on('tryon-warning', (msg) {
    final w = msg.toString();
    onLog?.call('⚠️ Warning: $w');
    onWarning?.call(w);
  });

  // ── Session ended ─────────────────────────────────────────────────────
  _socket!.on('tryon-session-ended', (_) {
    onLog?.call('🔴 Session ended');
    onSessionEnded?.call();
    _teardown();
  });

  _socket!.connect();
  onLog?.call('🔌 Connecting to socket…');
}