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,
    2. String,
    3. Map<String, dynamic>?
    )?,
  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>
    )?,
  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>().sendStackToRemote();
      ctx.read<TryOnDataUiProvider>().sendKioskRightPanel();
    }
  });

  // 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…');
}