INTEGRITY Документация

Управление состоянием

Предварительные требования

Эта страница опирается на Руководство по базовой реализации. Сначала обязательно изучите их.

Примеры кода на этой странице предполагают, что вы уже импортировали необходимые пакеты и инициализировали SDK.

Как компоненты UI Kit взаимодействуют друг с другом

Компоненты UI Kit способны понимать друг друга и синхронизироваться друг с другом, поскольку они вложены в RtkMeeting компонент. RtkMeeting компонент выступает в роли центрального координатора, который обеспечивает синхронизацию всех вложенных в него компонентов по состоянию встречи, обновлениям участников и другим изменениям в реальном времени.

Компоненты UI Kit способны понимать друг друга и синхронизироваться друг с другом, поскольку они вложены в rtk-meeting компонент. rtk-meeting компонент выступает в роли центрального координатора, который обеспечивает синхронизацию всех вложенных в него компонентов по состоянию встречи, обновлениям участников и другим изменениям в реальном времени.

Компоненты UI Kit способны понимать друг друга и синхронизироваться друг с другом, поскольку они вложены в rtk-meeting компонент. rtk-meeting компонент выступает в роли центрального координатора, который обеспечивает синхронизацию всех вложенных в него компонентов по состоянию встречи, обновлениям участников и другим изменениям в реальном времени.

Android UI Kit самостоятельно управляет взаимодействием компонентов. При сборке UI Kit с помощью RealtimeKitUIBuilder, он создаёт и координирует все необходимые UI-компоненты. Чтобы отслеживать изменения состояния встречи из вашего приложения, подключите слушатели к событиям Core SDK meeting объект.

iOS UI Kit внутренне управляет взаимодействием компонентов через RealtimeKitUI модуль. Чтобы отслеживать изменения состояния встречи из вашего приложения, реализуйте протоколы обработчиков событий и зарегистрируйте их в модуле Core SDK meeting объект.

Компоненты React Native UI Kit взаимодействуют и синхронизируются друг с другом, поскольку они вложены в RtkMeeting компонент, обёрнутый в RealtimeKitProvider и RtkUIProvider. Чтобы отслеживать изменения состояния, используйте хуки из Core SDK, например useRealtimeKitSelector.

Ниже приведен пример того, как работает синхронизация состояния при открытии боковой панели участников:

flowchart LR
    accTitle: Sidebar State Synchronization Example
    accDescr: Example showing how clicking participants toggle updates sidebar through meeting coordination

    Toggle["👤 ParticipantsToggle<br/>(User clicks)"]
    Meeting["Meeting Component<br/>(State Coordinator)"]
    Sidebar["Sidebar<br/>(Opens/Closes)"]
    App["Your App<br/>(Gets notified)"]

    Toggle -->|"emits rtkStateUpdate<br/>{activeSidebar: true,<br/>sidebar: 'participants'}"|Meeting
    Meeting -->|"propagates state"|Sidebar
    Meeting -->|"emits rtkStatesUpdate"|App

    style Meeting fill:#F48120,stroke:#333,stroke-width:2px,color:#fff
    style App fill:#0051C3,stroke:#333,stroke-width:2px,color:#fff

Поток состояний

  1. Дочерние компоненты передают обновления состояния: Когда любому UI-компоненту нужно обновить состояние, он генерирует rtkStateUpdate событие
  2. Компонент встречи прослушивает события и координирует их: Компонент встречи прослушивает все эти события обновления состояния от своих дочерних компонентов
  3. Распространение состояния: Компонент встречи передает обновленное состояние всем остальным дочерним компонентам, чтобы поддерживать их синхронизацию
  4. Внешнее уведомление: Компонент встречи также генерирует rtkStatesUpdate событие, на которое приложение может подписаться, чтобы обновлять собственный интерфейс или выполнять действия при изменении состояния
  1. UI Kit управляет внутренним состоянием: UI Kit самостоятельно обрабатывает взаимодействие компонентов и синхронизацию состояния
  2. Ваше приложение регистрирует слушателей событий: Вы подключаете обработчики событий (например, RtkMeetingRoomEventListener и RtkSelfEventListener) к элементу Core SDK meeting объект
  3. Колбэки срабатывают при изменении состояния: При изменении состояния встречи (например, когда участник присоединяется или переключается звук) вызывается соответствующий обработчик
  4. Вы обновляете свой интерфейс: Используйте данные обратного вызова, чтобы обновить интерфейс приложения или запустить другие действия
  1. UI Kit управляет внутренним состоянием: RtkMeeting компонент отвечает за всё внутреннее взаимодействие компонентов и синхронизацию состояния
  2. Ваше приложение отслеживает состояние с помощью хуков: Используйте useRealtimeKitSelector чтобы выбирать конкретные свойства встречи и перерисовываться при их изменении
  3. React выполняет повторный рендеринг при изменениях: При изменении выбранного значения React автоматически перерисовывает компонент с новым состоянием
  4. Вы обновляете свой интерфейс: Используйте наблюдаемые значения состояния, чтобы условно отображать элементы интерфейса или запускать побочные эффекты

Отслеживание обновлений состояния

Чтобы создать пользовательский интерфейс или выполнять действия на основе изменений состояния встречи, необходимо отслеживать обновления состояния из UI Kit.

Прослушивайте rtkStatesUpdate событие, генерируемое компонентом meeting. Оно передаёт текущее состояние UI Kit, включая состояние боковой панели, статус демонстрации экрана, тип отображения и другие параметры.

Подключите слушатели к событиям Core SDK meeting объект, чтобы отслеживать изменения состояния встречи. Мобильный UI Kit управляет собственным внутренним состоянием, поэтому ваше приложение напрямую взаимодействует с базовым объектом встречи.

Используйте useRealtimeKitSelector хук из @cloudflare/realtimekit-react-native чтобы отслеживать отдельные свойства объекта встречи. Этот хук перерисовывает ваш компонент при каждом изменении выбранного значения, аналогично тому, как работают селекторы в библиотеках управления состоянием.

Пример кода

Для React можно использовать onRtkStatesUpdate prop в RtkMeeting компонент для отслеживания обновлений состояния.

import {
	RealtimeKitProvider,
	useRealtimeKitClient,
} from "@cloudflare/realtimekit-react";
import { RtkMeeting } from "@cloudflare/realtimekit-react-ui";
import { useEffect, useState } from "react";

function App() {
	const [meeting, initMeeting] = useRealtimeKitClient();
	const [authToken, setAuthToken] = useState("<participant_auth_token>");
	const [states, setStates] = useState({});

	useEffect(() => {
		if (authToken) {
			initMeeting({
				authToken: authToken,
			});
		}
	}, [authToken]);

	return (
		<RealtimeKitProvider value={meeting}>
			<RtkMeeting
				showSetupScreen={true}
				meeting={meeting}
				onRtkStatesUpdate={(e) => {
					// Update states when rtk-meeting emits state updates
					setStates(e.detail);

					// Example: Access various state properties
					console.log("Meeting state:", e.detail.meeting); // 'idle', 'setup', 'joined', 'ended', 'waiting'
					console.log("Is sidebar active:", e.detail.activeSidebar);
					console.log("Current sidebar section:", e.detail.sidebar);
					console.log("Is screen sharing:", e.detail.activeScreenShare);
				}}
			/>

			{/* Use states to build custom UI */}
			<div className="custom-ui">
				<p>Meeting State: {states.meeting}</p>
				<p>Sidebar Open: {states.activeSidebar ? "Yes" : "No"}</p>
			</div>
		</RealtimeKitProvider>
	);
}

Альтернатива: использование Refs (несколько встреч)

Если вы создаёте сценарий с несколькими встречами на одной странице или последовательными встречами, для предотвращения конфликтов состояния между разными экземплярами встречи рекомендуется использовать refs:

import {
	RealtimeKitProvider,
	useRealtimeKitClient,
} from "@cloudflare/realtimekit-react";
import { RtkMeeting } from "@cloudflare/realtimekit-react-ui";
import { useEffect, useState, useRef } from "react";

function App() {
	const [meeting, initMeeting] = useRealtimeKitClient();
	const [authToken, setAuthToken] = useState("<participant_auth_token>");
	const [states, setStates] = useState({});
	const meetingRef = useRef(null);

	useEffect(() => {
		if (authToken) {
			initMeeting({
				authToken: authToken,
			});
		}
	}, [authToken]);

	useEffect(() => {
		if (!meetingRef.current) return;

		const handleStatesUpdate = (e) => {
			setStates(e.detail);
			console.log("Meeting state:", e.detail.meeting);
			console.log("Is sidebar active:", e.detail.activeSidebar);
		};

		// Add event listener via ref
		meetingRef.current.addEventListener("rtkStatesUpdate", handleStatesUpdate);

		// Cleanup listener when component unmounts or meeting changes
		return () => {
			meetingRef.current?.removeEventListener(
				"rtkStatesUpdate",
				handleStatesUpdate,
			);
		};
	}, [meetingRef.current]);

	return (
		<RealtimeKitProvider value={meeting}>
			<RtkMeeting ref={meetingRef} showSetupScreen={true} meeting={meeting} />

			{/* Use states to build custom UI */}
			<div className="custom-ui">
				<p>Meeting State: {states.meeting}</p>
				<p>Sidebar Open: {states.activeSidebar ? "Yes" : "No"}</p>
			</div>
		</RealtimeKitProvider>
	);
}

Для Web Components необходимо добавить обработчик события к rtk-meeting компонент для отслеживания rtkStatesUpdate события.

<body>
	<rtk-meeting id="meeting-component"></rtk-meeting>
</body>
<script type="module">
	import RealtimeKitClient from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/index.es.js";

	const meeting = await RealtimeKitClient.init({
		authToken: "<participant_auth_token>",
	});

	// Add <rtk-meeting id="meeting-component" /> to your HTML, otherwise you will get error
	const meetingComponent = document.querySelector("#meeting-component");

	// Listen for state updates from rtk-meeting
	meetingComponent.addEventListener("rtkStatesUpdate", (event) => {
		console.log("RTK states updated:", event.detail);

		// Store states to update your custom UI
		const states = event.detail;

		// Example: Access various state properties
		console.log("Meeting state:", states.meeting); // 'idle', 'setup', 'joined', 'ended', 'waiting'
		console.log("Is sidebar active:", states.activeSidebar);
		console.log("Current sidebar section:", states.sidebar); // 'chat', 'participants', 'polls', etc.
		console.log("Is screen sharing:", states.activeScreenShare);

		// Update your custom UI based on states
		// For example: Show/hide elements based on meeting state
		if (states.meeting === "joined") {
			// Show meeting controls
		}
	});

	meetingComponent.showSetupScreen = true;
	meetingComponent.meeting = meeting;
</script>

Для Angular необходимо добавить обработчик события к rtk-meeting компонент для отслеживания rtkStatesUpdate события.

meeting.component.ts
import {
	Component,
	ElementRef,
	OnInit,
	OnDestroy,
	ViewChild,
} from "@angular/core";

@Component({
	selector: "app-meeting",
	template: `
		<rtk-meeting #meetingComponent id="meeting-component"></rtk-meeting>

		<!-- Use states to build custom UI -->
		<div class="custom-ui" *ngIf="states">
			<p>Meeting State: {{ states.meeting }}</p>
			<p>Sidebar Open: {{ states.activeSidebar ? "Yes" : "No" }}</p>
			<div *ngIf="states.meeting === 'joined'" class="meeting-controls">
				<!-- Show meeting controls when joined -->
				<p>Meeting controls would go here</p>
			</div>
		</div>
	`,
	styleUrls: ["./meeting.component.css"],
})
export class MeetingComponent implements OnInit, OnDestroy {
	@ViewChild("meetingComponent", { static: true }) meetingElement!: ElementRef;

	meeting: any;
	states: any = {};
	private authToken = "<participant_auth_token>";
	private stateUpdateListener?: (event: any) => void;

	async ngOnInit() {
		// Import RealtimeKit client dynamically
		const RealtimeKitClient = await import(
			"https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/index.es.js"
		);

		// Initialize the meeting
		this.meeting = await RealtimeKitClient.default.init({
			authToken: this.authToken,
		});

		// Set up the meeting component
		const meetingComponent = this.meetingElement.nativeElement;

		// Create the event listener
		this.stateUpdateListener = (event: any) => {
			console.log("RTK states updated:", event.detail);

			// Store states to update your custom UI
			this.states = event.detail;

			// Example: Access various state properties
			console.log("Meeting state:", this.states.meeting); // 'idle', 'setup', 'joined', 'ended', 'waiting'
			console.log("Is sidebar active:", this.states.activeSidebar);
			console.log("Current sidebar section:", this.states.sidebar); // 'chat', 'participants', 'polls', etc.
			console.log("Is screen sharing:", this.states.activeScreenShare);

			// Update your custom UI based on states
			// For example: Show/hide elements based on meeting state
			if (this.states.meeting === "joined") {
				// Show meeting controls
				console.log("Meeting joined - showing controls");
			}
		};

		// Listen for state updates from rtk-meeting
		meetingComponent.addEventListener(
			"rtkStatesUpdate",
			this.stateUpdateListener,
		);

		// Configure the meeting component
		meetingComponent.showSetupScreen = true;
		meetingComponent.meeting = this.meeting;
	}

	ngOnDestroy() {
		// Clean up event listener when component is destroyed
		if (this.stateUpdateListener && this.meetingElement) {
			this.meetingElement.nativeElement.removeEventListener(
				"rtkStatesUpdate",
				this.stateUpdateListener,
			);
		}
	}
}

Для Android подключите слушатели к событиям meeting объект, чтобы отслеживать изменения состояния. Используйте RtkMeetingRoomEventListener для событий жизненного цикла встречи и RtkSelfEventListener для изменений состояния локального участника.

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity

class MeetingActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // After initializing the meeting and UI Kit (see Getting Started guide),
        // add event listeners to observe state changes.

        // Listen for meeting room state changes
        meeting.addMeetingRoomEventListener(object : RtkMeetingRoomEventListener {
            override fun onMeetingRoomJoinStarted() {
                Log.d("Meeting", "Join started")
            }

            override fun onMeetingRoomJoinCompleted(meeting: RealtimeKitClient) {
                Log.d("Meeting", "Joined the meeting")
                // Update UI to show meeting controls
            }

            override fun onMeetingRoomJoinFailed(exception: Exception) {
                Log.e("Meeting", "Join failed: ${exception.message}")
            }

            override fun onMeetingRoomLeaveStarted() {
                Log.d("Meeting", "Leave started")
            }

            override fun onMeetingRoomLeft() {
                Log.d("Meeting", "Left the meeting")
            }

            override fun onMeetingEnded() {
                Log.d("Meeting", "Meeting ended for all participants")
            }

            override fun onActiveTabUpdate(activeTab: ActiveTab) {
                Log.d("Meeting", "Active tab changed: $activeTab")
            }
        })

        // Listen for local participant state changes
        meeting.addSelfEventListener(object : RtkSelfEventListener {
            override fun onAudioUpdate(isEnabled: Boolean) {
                Log.d("Meeting", "Audio: ${if (isEnabled) "on" else "off"}")
            }

            override fun onVideoUpdate(isEnabled: Boolean) {
                Log.d("Meeting", "Video: ${if (isEnabled) "on" else "off"}")
            }

            override fun onRemovedFromMeeting() {
                Log.d("Meeting", "Removed from meeting by host")
            }
        })
    }
}

Для iOS реализуйте протоколы обработчиков событий и зарегистрируйте их в meeting объект. Используйте RtkMeetingRoomEventListener для событий жизненного цикла встречи и RtkSelfEventListener для изменений состояния локального участника.

// Listen for meeting room state changes
extension MeetingViewModel: RtkMeetingRoomEventListener {
    func onMeetingRoomJoinCompleted(meeting: RealtimeKitClient) {
        // Successfully joined the meeting (equivalent to 'joined' state)
    }

    func onMeetingRoomLeft() {
        // Successfully left the meeting
    }

    func onMeetingEnded() {
        // The meeting has ended for all participants (equivalent to 'ended' state)
    }

    func onActiveTabUpdate(activeTab: ActiveTab) {
        // Active tab changed (e.g., chat, polls, participants)
        // Use this to sync your custom UI with the active sidebar section
    }
}

// Listen for local participant state changes
extension MeetingViewModel: RtkSelfEventListener {
    func onAudioUpdate(isEnabled: Bool) {
        // Audio toggled on/off
    }

    func onVideoUpdate(isEnabled: Bool) {
        // Video toggled on/off
    }

    func onRemovedFromMeeting() {
        // Local user was removed from the meeting by host
    }
}

// Register the listeners
meeting.addMeetingRoomEventListener(meetingRoomEventListener: self)
meeting.addSelfEventListener(selfEventListener: self)

Для React Native используйте useRealtimeKitSelector хук для отслеживания отдельных свойств объекта встречи. Этот подход аналогичен веб-версии Core SDK.

import { useEffect } from "react";
import { View, Text } from "react-native";
import {
	RealtimeKitProvider,
	useRealtimeKitClient,
	useRealtimeKitMeeting,
	useRealtimeKitSelector,
} from "@cloudflare/realtimekit-react-native";
import {
	RtkUIProvider,
	RtkMeeting,
} from "@cloudflare/realtimekit-react-native-ui";

function App() {
	const [meeting, initMeeting] = useRealtimeKitClient();

	useEffect(() => {
		initMeeting({
			authToken: "<participant_auth_token>",
			defaults: { audio: true, video: true },
		});
	}, []);

	return (
		<RealtimeKitProvider value={meeting}>
			<RtkUIProvider>
				<MeetingWithState />
			</RtkUIProvider>
		</RealtimeKitProvider>
	);
}

function MeetingWithState() {
	const { meeting } = useRealtimeKitMeeting();

	// Use selectors to observe meeting state
	const roomState = useRealtimeKitSelector((m) => m.self.roomState);
	const audioEnabled = useRealtimeKitSelector((m) => m.self.audioEnabled);
	const videoEnabled = useRealtimeKitSelector((m) => m.self.videoEnabled);

	useEffect(() => {
		console.log("Room state:", roomState);
		console.log("Audio:", audioEnabled);
		console.log("Video:", videoEnabled);
	}, [roomState, audioEnabled, videoEnabled]);

	return (
		<View>
			{meeting && <RtkMeeting meeting={meeting} showSetupScreen={true} />}

			{/* Use state to build custom UI */}
			<View>
				<Text>Room State: {roomState}</Text>
				<Text>Audio: {audioEnabled ? "On" : "Off"}</Text>
				<Text>Video: {videoEnabled ? "On" : "Off"}</Text>
			</View>
		</View>
	);
}

Альтернатива: использование обработчиков событий

Для более точного контроля также можно использовать слушателей на основе событий, как в веб Core SDK:

import { useEffect } from "react";

function MeetingEvents() {
	const { meeting } = useRealtimeKitMeeting();

	useEffect(() => {
		if (!meeting) return;

		const handleRoomJoined = () => {
			console.log("Successfully joined the meeting");
		};

		const handleRoomLeft = ({ state }) => {
			if (state === "ended") {
				console.log("Meeting ended");
			}
		};

		meeting.self.on("roomJoined", handleRoomJoined);
		meeting.self.on("roomLeft", handleRoomLeft);

		return () => {
			meeting.self.removeListener("roomJoined", handleRoomJoined);
			meeting.self.removeListener("roomLeft", handleRoomLeft);
		};
	}, [meeting]);

	return null;
}

Свойства состояния

rtkStatesUpdate событие содержит подробную информацию о внутреннем состоянии UI Kit. Основные свойства:

На мобильных платформах состояние отслеживается через обработчики событий Core SDK, а не через единый объект состояния. Основные обработчики событий и их колбэки:

RtkMeetingRoomEventListener - Жизненный цикл встречи:

RtkSelfEventListener - Локальный участник:

RtkParticipantsEventListener - Удалённые участники:

В React Native используйте useRealtimeKitSelector хук для отслеживания отдельных свойств объекта встречи. Основные свойства:

Лучшие практики