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

Создание собственного интерфейса

Это руководство объясняет, как использовать компоненты RealtimeKit UI Kit для создания пользовательского интерфейса встречи вместо стандартного полноэкранного вида встречи.

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

Эта страница опирается на Инициализируйте SDK, Отрисовка UI собрания по умолчанию, а также Состояния UI Kit руководства. Сначала обратитесь к этим страницам, чтобы понять основные концепции.

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

Создание пользовательского интерфейса с помощью UI Kit

Если стандартный компонент встречи не дает достаточного контроля над макетом или поведением, используйте компоненты UI Kit для создания собственного интерфейса. UI Kit предоставляет готовые компоненты поверх Core SDK. Вы можете комбинировать их по своему усмотрению, экономя время по сравнению с созданием интерфейса с нуля.

При использовании собственного интерфейса вам придётся самостоятельно управлять звуком участников, уведомлениями, диалогами, расположением компонентов и переходами между экранами.

Аналогично rtk-meeting, rtk-ui-provider, ещё один компонент UI Kit, выполняющий роль провайдера, также прослушивает состояния и синхронизирует их с компонентами UI Kit.

В отличие от rtk-meeting, rtk-ui-provider позволяет передавать в него любые дочерние компоненты. Если какой-либо дочерний компонент относится к RealtimeKit и его имя начинается с rtk-, rtk-ui-provider будет координировать работу с ним для синхронизации состояний.

Аналогично RtkMeeting, RtkUiProvider, ещё один компонент UI Kit, выполняющий роль провайдера, также прослушивает состояния и синхронизирует их с компонентами UI Kit.

В отличие от RtkMeeting, RtkUiProvider позволяет передавать в него любые дочерние компоненты. Если какой-либо дочерний компонент относится к RealtimeKit и его имя начинается с Rtk, RtkUiProvider будет координировать работу с ним для синхронизации состояний.

Аналогично rtk-meeting, rtk-ui-provider, ещё один компонент UI Kit, выполняющий роль провайдера, также прослушивает состояния и синхронизирует их с компонентами UI Kit.

В отличие от rtk-meeting, rtk-ui-provider позволяет передавать в него любые дочерние компоненты. Если какой-либо дочерний компонент относится к RealtimeKit и его имя начинается с rtk-, rtk-ui-provider будет координировать работу с ним для синхронизации состояний.

iOS SDK использует паттерн делегирования для пользовательских интерфейсов. Вместо оборачивания дочерних представлений в провайдер вы реализуете RtkUIFlowCoordinatorDelegate и вернуть собственные view controller для каждого экрана в сценарии встречи.

Существует два уровня настройки:

public protocol RtkUIFlowCoordinatorDelegate {
    func showSetUpScreen(completion: () -> Void) -> SetupViewControllerDataSource?
    func showGroupCallMeetingScreen(meeting: RealtimeKitClient, completion: @escaping () -> Void) -> UIViewController?
    func showWebinarMeetingScreen(meeting: RealtimeKitClient, completion: @escaping () -> Void) -> UIViewController?
}

Когда пользователь успешно присоединяется, SDK вызывает showGroupCallMeetingScreen(meeting:completion:) или showWebinarMeetingScreen(meeting:completion:). Используйте RealtimeKitClient параметр для привязки отдельных компонентов UI Kit (таких как RtkMeetingHeaderView, RtkMeetingControlBar, или RtkParticipantTileView) ко встрече.

Android SDK не использует паттерн provider/wrapper. Вместо этого каждый отдельный View-компонент содержит activate(meeting) метод, который привязывает его ко встрече.

Шаблон:

  1. Создайте RealtimeKitUI экземпляр с RealtimeKitUIBuilder.build().
  2. Откройте RealtimeKitClient через rtkUI.meeting.
  3. Размещайте отдельные представления UI Kit в собственном XML-макете или создавайте их в коде.
  4. Вызовите activate(meeting) на каждом компоненте, чтобы привязать его к встрече.

Компоненты уровня встречи (сетка, заголовок, панель управления) принимают RealtimeKitClient:

gridView.activate(meeting)
headerView.activate(meeting)
controlBarView.activate(meeting)

Компоненты на уровне участника (плитка, аватар, плашка с именем) принимают RtkMeetingParticipant:

tileView.activate(meeting.localUser)
tileView.activate(remoteParticipant)

Аналогично RtkMeeting, RtkUIProvider это компонент-обёртка, который предоставляет дизайн-систему и контекст состояния интерфейса всем дочерним компонентам.

В отличие от RtkMeeting, RtkUIProvider позволяет передавать в него любые дочерние компоненты. Все компоненты RealtimeKit UI Kit считывают данные из RtkUIContext для токенов дизайна и состояния.

Используйте useContext(RtkUIContext) чтобы прочитать storeStates.meeting и определить, какой экран отобразить. При разработке без RtkMeeting, вы должны прослушивать события SDK и вызывать setStates чтобы самостоятельно управлять переходами состояний.

Пример кода

<!DOCTYPE html>
<html>
	<head>
		<script type="module">
			import { defineCustomElements } from "https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui@latest/loader/index.es2017.js";
			defineCustomElements();
		</script>
		<script src="https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/browser.js"></script>
	</head>

    <body style="margin: 0;">
    	<rtk-ui-provider
    		id="rtk-ui-provider"
    		style="display: flex; flex-direction: column; height: 100vh; margin: 0;"
    	>
    		<div
    			id="meeting-container"
    			style="display: flex; flex-direction: column; flex: 1; flex-grow: 1; flex-shrink: 1;"
    		>
    			Meeting will render here...
    		</div>
    		<rtk-participants-audio></rtk-participants-audio>
    		<rtk-dialog-manager></rtk-dialog-manager>
    		<rtk-notifications></rtk-notifications>
    	</rtk-ui-provider>
    	<script type="module">
    		async function initializeMeeting() {
    			let currentState = "idle";

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

    			function renderSetupScreen() {
    				document.querySelector("#meeting-container").innerHTML = `
                    <rtk-setup-screen></rtk-setup-screen>
                `;
    			}

    			function renderWaitingScreen() {
    				document.querySelector("#meeting-container").innerHTML = `
                    <rtk-waiting-screen></rtk-waiting-screen>
                `;
    			}

    			function renderJoinedScreen() {
    				document.querySelector("#meeting-container").innerHTML = `
                    <rtk-header style="display: flex; justify-content: space-between;"></rtk-header>
                    <rtk-stage style="flex: 1; flex-grow: 1; flex-shrink: 1;">
                        <rtk-grid></rtk-grid>
                        <rtk-sidebar style="position: fixed; top:0px;"></rtk-sidebar>
                    </rtk-stage>
                    <rtk-controlbar style="display: flex; justify-content: space-between;"></rtk-controlbar>
                `;
    			}

    			function renderEndedScreen() {
    				document.querySelector("#meeting-container").innerHTML = `
                    <rtk-ended-screen></rtk-ended-screen>
                `;
    			}

    			// Listen for state updates from rtk-ui-provider
    			document
    				.querySelector("rtk-ui-provider")
    				.addEventListener("rtkStatesUpdate", (event) => {
    					// Store states to update your custom UI
    					const states = event.detail;

    					if (states.meeting === "idle" && currentState !== "idle") {
    						currentState = "idle";
    						document
    							.querySelector("rtk-ui-provider")
    							.querySelector("#meeting-container").innerHTML =
    							"Meeting is loading...";
    					} else if (states.meeting === "setup" && currentState !== "setup") {
    						currentState = "setup";
    						renderSetupScreen();
    					} else if (
    						states.meeting === "waiting" &&
    						currentState !== "waiting"
    					) {
    						currentState = "waiting";
    						renderWaitingScreen();
    					} else if (
    						states.meeting === "joined" &&
    						currentState !== "joined"
    					) {
    						currentState = "joined";
    						renderJoinedScreen();
    					} else if (states.meeting === "ended" && currentState !== "ended") {
    						currentState = "ended";
    						renderEndedScreen();
    					}

    					const sidebarComponent = document
    						.querySelector("rtk-ui-provider")
    						.querySelector("#meeting-container")
    						.querySelector("rtk-sidebar");
    					if (sidebarComponent) {
    						if (states.activeSidebar) {
    							sidebarComponent.style.display = "block";
    						} else {
    							sidebarComponent.style.display = "none";
    						}
    					}
    				});

    			document.querySelector("rtk-ui-provider").showSetupScreen = true;
    			document.querySelector("rtk-ui-provider").meeting = meeting;
    		}
    		initializeMeeting();
    	</script>
    </body>

</html>

Разделение первого уровня для rtk-meeting с помощью rtk-ui-provider включает следующие компоненты:

rtk-header представляет собой компонент заголовка, отображающий название сессии и элементы управления сессией.
rtk-stage представляет собой компонент-контейнер, включающий в себя компоненты сетки и боковой панели.
rtk-grid представляет собой компонент сетки, отображающий участников сессии.
rtk-sidebar представляет собой компонент боковой панели, в которой отображается содержимое чата и опросов.
rtk-controlbar представляет собой компонент controlbar, отображающий элементы управления, такие как камера, микрофон и т. д.
rtk-notifications представляет собой компонент уведомлений, отображающий уведомления сессии.
rtk-participants-audio представляет собой аудиокомпонент, который позволяет слышать других участников сессии.
rtk-dialog-manager представляет собой компонент dialog-manager, отображающий все поддерживаемые диалоговые окна, такие как настройки, сессионные залы и т. д.

Вы можете разделить все эти компоненты ещё детальнее. Чтобы увидеть больше таких компонентов, обратитесь к нашей библиотека компонентов.

Наш UI Kit опубликован с открытым исходным кодом на GitHub, найти его можно здесь.

import {
	RealtimeKitProvider,
	useRealtimeKitClient,
} from "@cloudflare/realtimekit-react";
import {
	RtkUiProvider,
	RtkHeader,
	RtkStage,
	RtkGrid,
	RtkSidebar,
	RtkControlbar,
	RtkNotifications,
	RtkParticipantsAudio,
	RtkDialogManager,
	RtkSetupScreen,
	RtkWaitingScreen,
	RtkEndedScreen,
	States,
} from "@cloudflare/realtimekit-react-ui";
import { useEffect, useState } from "react";

function MeetingContainer() {
	const [meeting, initMeeting] = useRealtimeKitClient();
	const [currentState, setCurrentState] = useState("idle");
	const [showSidebar, setShowSidebar] = useState(false);

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

	const renderSetupScreen = () => {
		return <RtkSetupScreen />;
	};

	const renderWaitingScreen = () => {
		return <RtkWaitingScreen />;
	};

	const renderJoinedScreen = () => {
		return (
			<>
				<RtkHeader
					style={{ display: "flex", justifyContent: "space-between" }}
				/>
				<RtkStage style={{ flex: 1, flexGrow: 1, flexShrink: 1 }}>
					<RtkGrid />
					<RtkSidebar
						style={{
							position: "fixed",
							top: "0px",
							display: showSidebar ? "block" : "none",
						}}
					/>
				</RtkStage>
				<RtkControlbar
					style={{ display: "flex", justifyContent: "space-between" }}
				/>
			</>
		);
	};

	const renderEndedScreen = () => {
		return <RtkEndedScreen />;
	};

	// Listen for state updates from RtkUiProvider
	const handleStatesUpdate = (event: { detail: States }) => {
		const meetingState = event.detail.meeting;
		const states = event.detail;

		// Store states to update your custom UI
		if (meetingState === "idle" && currentState !== "idle") {
			setCurrentState("idle");
		} else if (meetingState === "setup" && currentState !== "setup") {
			setCurrentState("setup");
		} else if (meetingState === "waiting" && currentState !== "waiting") {
			setCurrentState("waiting");
		} else if (meetingState === "joined" && currentState !== "joined") {
			setCurrentState("joined");
		} else if (meetingState === "ended" && currentState !== "ended") {
			setCurrentState("ended");
		}

		// Update sidebar visibility based on state
		if (states.activeSidebar !== undefined) {
			setShowSidebar(states.activeSidebar);
		}
	};

	return (
		<RealtimeKitProvider value={meeting}>
			<RtkUiProvider
				meeting={meeting}
				showSetupScreen={true}
				onRtkStatesUpdate={handleStatesUpdate}
				style={{
					display: "flex",
					flexDirection: "column",
					height: "100vh",
					margin: 0,
				}}
			>
				<div
					id="meeting-container"
					style={{
						display: "flex",
						flexDirection: "column",
						flex: 1,
						flexGrow: 1,
						flexShrink: 1,
					}}
				>
					{currentState === "idle" && <div>Meeting is loading...</div>}
					{currentState === "setup" && renderSetupScreen()}
					{currentState === "waiting" && renderWaitingScreen()}
					{currentState === "joined" && renderJoinedScreen()}
					{currentState === "ended" && renderEndedScreen()}
				</div>
				<RtkParticipantsAudio />
				<RtkDialogManager />
				<RtkNotifications />
			</RtkUiProvider>
		</RealtimeKitProvider>
	);
}

function App() {
	return <MeetingContainer />;
}

Разделение первого уровня для RtkMeeting с помощью RtkUiProvider включает следующие компоненты:

RtkHeader представляет собой компонент заголовка, отображающий название сессии и элементы управления сессией.
RtkStage представляет собой компонент-контейнер, включающий в себя компоненты сетки и боковой панели.
RtkGrid представляет собой компонент сетки, отображающий участников сессии.
RtkSidebar представляет собой компонент боковой панели, в которой отображается содержимое чата и опросов.
RtkControlbar представляет собой компонент controlbar, отображающий элементы управления, такие как камера, микрофон и т. д.
RtkNotifications представляет собой компонент уведомлений, отображающий уведомления сессии.
RtkParticipantsAudio представляет собой аудиокомпонент, который позволяет слышать других участников сессии.
RtkDialogManager представляет собой компонент dialog-manager, отображающий диалоговые окна сессии.

Вы можете разделить все эти компоненты ещё детальнее. Чтобы увидеть больше таких компонентов, обратитесь к нашей библиотека компонентов.

Наш UI Kit опубликован с открытым исходным кодом на GitHub, найти его можно здесь.

В файле app.module.ts импортируйте RealtimeKitComponentsModule вместе со всеми собственными модулями, которые вы создали с помощью библиотеки компонентов (пример приведен ниже).

app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";

import { AppComponent } from "./app.component";
import { CustomRtkMeetingComponent } from "./components/custom-rtk-meeting.component";
import { SetupScreenComponent } from "./components/setup-screen.component";
import { InMeetingComponent } from "./components/in-meeting.component";
import { MeetingHeaderComponent } from "./components/meeting-header.component";
import { MeetingControlBarComponent } from "./components/meeting-control-bar.component";
import { MeetingSidebarComponent } from "./components/meeting-sidebar.component";
import { MediaPreviewModalComponent } from "./components/media-preview-modal.component";
import { AudioPreviewComponent } from "./components/audio-preview.component";
import { VideoPreviewComponent } from "./components/video-preview.component";

import { RealtimeKitComponentsModule } from "@cloudflare/realtimekit-angular-ui";

@NgModule({
	declarations: [
		AppComponent,
		CustomRtkMeetingComponent,
		SetupScreenComponent,
		InMeetingComponent,
		MeetingHeaderComponent,
		MeetingControlBarComponent,
		MeetingSidebarComponent,
		MediaPreviewModalComponent,
		AudioPreviewComponent,
		VideoPreviewComponent,
	],
	imports: [BrowserModule, FormsModule, RealtimeKitComponentsModule],
	providers: [],
	bootstrap: [AppComponent],
})
export class AppModule {}

Инициализируйте встречу в файле app.component.ts

app.component.ts
import { Component, OnInit, Inject } from "@angular/core";
import { DOCUMENT } from "@angular/common";
import { StatesService } from "./services/states.service";
import RealtimeKitClient from "@cloudflare/realtimekit";

@Component({
	selector: "app-root",
	templateUrl: "./app.component.html",
	styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit {
	meeting: any = null;

	constructor(
		private statesService: StatesService,
		@Inject(DOCUMENT) private document: Document,
	) {}

	async ngOnInit() {
		await this.initializeMeeting();
	}

	private async initializeMeeting() {
		const searchParams = new URLSearchParams(
			this.document.defaultView?.location.search,
		);
		const authToken = searchParams.get("authToken");

		if (!authToken) {
			alert(
				"An authToken wasn't passed, please pass an authToken in the URL query to join a meeting.",
			);
			return;
		}

		if (!this.meeting) {
			try {
				// Initialize RealtimeKit client
				const meeting = await RealtimeKitClient.init({
					authToken,
				});

				this.meeting = meeting;

				// Expose meeting object to window for debugging
				Object.assign(this.document.defaultView as any, {
					meeting: this.meeting,
				});
			} catch (error) {
				console.error("Failed to initialize meeting:", error);
			}
		}
	}

	onRtkStatesUpdate(event: any) {
		this.statesService.setStates(event.detail);
	}
}
app.component.html
<rtk-ui-provider
	[meeting]="meeting"
	(rtkStatesUpdate)="onRtkStatesUpdate($event)"
	[showSetupScreen]="true"
	style="height: 100%; width: 100%; display: block;"
>
	<app-custom-rtk-meeting></app-custom-rtk-meeting>
	<rtk-dialog-manager></rtk-dialog-manager>
</rtk-ui-provider>

Вот пример того, как выглядит пользовательский элемент, созданный с использованием компонентов UI Kit:

components/custom-rtk-component.ts
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Subject, takeUntil } from "rxjs";
import { StatesService, CustomStatesService } from "../services/states.service";
import { States } from "@cloudflare/realtimekit-ui";
import { CustomStates } from "../types";

@Component({
	selector: "app-custom-rtk-meeting",
	template: `
		<rtk-idle-screen *ngIf="states.meeting === 'idle'"></rtk-idle-screen>
		<app-setup-screen *ngIf="states.meeting === 'setup'"></app-setup-screen>
		<rtk-waiting-screen
			*ngIf="states.meeting === 'waiting'"
		></rtk-waiting-screen>
		<rtk-ended-screen *ngIf="states.meeting === 'ended'"></rtk-ended-screen>
		<app-in-meeting
			*ngIf="states.meeting === 'joined' || !states.meeting"
		></app-in-meeting>
	`,
})
export class CustomRtkMeetingComponent implements OnInit, OnDestroy {
	states: States = { meeting: "idle" } as States;
	customStates: CustomStates = {};
	private destroy$ = new Subject<void>();

	constructor(
		private statesService: StatesService,
		private customStatesService: CustomStatesService,
	) {}

	ngOnInit() {
		this.statesService.states$
			.pipe(takeUntil(this.destroy$))
			.subscribe((states) => {
				this.states = states;
				console.log(states, this.customStates);
			});

		this.customStatesService.customStates$
			.pipe(takeUntil(this.destroy$))
			.subscribe((customStates) => {
				this.customStates = customStates;
				console.log(this.states, customStates);
			});
	}

	ngOnDestroy() {
		this.destroy$.next();
		this.destroy$.complete();
	}
}

Разделение первого уровня для rtk-meeting с помощью rtk-ui-provider включает следующие компоненты:

rtk-header представляет собой компонент заголовка, отображающий название сессии и элементы управления сессией.
rtk-stage представляет собой компонент-контейнер, включающий в себя компоненты сетки и боковой панели.
rtk-grid представляет собой компонент сетки, отображающий участников сессии.
rtk-sidebar представляет собой компонент боковой панели, в которой отображается содержимое чата и опросов.
rtk-controlbar представляет собой компонент controlbar, отображающий элементы управления, такие как камера, микрофон и т. д.
rtk-notifications представляет собой компонент уведомлений, отображающий уведомления сессии.
rtk-participants-audio представляет собой аудиокомпонент, который позволяет слышать других участников сессии.
rtk-dialog-manager представляет собой компонент dialog-manager, отображающий все поддерживаемые диалоговые окна, такие как настройки, сессионные залы и т. д.

Вы можете разделить все эти компоненты ещё детальнее. Чтобы увидеть больше таких компонентов, обратитесь к нашей библиотека компонентов.

Наш UI Kit опубликован с открытым исходным кодом на GitHub, найти его можно здесь.

В следующем примере экран встречи с групповым звонком заменяется пользовательским view controller, который использует отдельные компоненты UI Kit:

import UIKit
import RealtimeKit
import RealtimeKitUI

class CustomFlowDelegate: RtkUIFlowCoordinatorDelegate {
    // Return nil to use the default setup screen
    func showSetUpScreen(completion: () -> Void) -> SetupViewControllerDataSource? {
        return nil
    }

    // Return a custom view controller for group calls
    func showGroupCallMeetingScreen(
        meeting: RealtimeKitClient,
        completion: @escaping () -> Void
    ) -> UIViewController? {
        return CustomMeetingViewController(meeting: meeting, completion: completion)
    }

    // Return nil to use the default webinar screen
    func showWebinarMeetingScreen(
        meeting: RealtimeKitClient,
        completion: @escaping () -> Void
    ) -> UIViewController? {
        return nil
    }
}

class CustomMeetingViewController: UIViewController {
    private let meeting: RealtimeKitClient
    private let completion: () -> Void
    private var selfListener: RtkEventSelfListener?

    init(meeting: RealtimeKitClient, completion: @escaping () -> Void) {
        self.meeting = meeting
        self.completion = completion
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) { fatalError("init(coder:) not supported") }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black

        // Header
        let header = RtkMeetingHeaderView(meeting: meeting)
        header.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(header)

        // Control bar
        let controlBar = RtkMeetingControlBar(
            meeting: meeting,
            delegate: nil,
            presentingViewController: self
        )
        controlBar.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(controlBar)

        NSLayoutConstraint.activate([
            header.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            header.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            header.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            header.heightAnchor.constraint(equalToConstant: 50),
            controlBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            controlBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            controlBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            controlBar.heightAnchor.constraint(equalToConstant: 80),
        ])

        // Listen for meeting events
        selfListener = RtkEventSelfListener(rtkClient: meeting)
        selfListener?.observeSelfRemoved { [weak self] _ in
            self?.dismiss(animated: true, completion: self?.completion)
        }
        selfListener?.observeSelfMeetingEndForAll { [weak self] _ in
            self?.dismiss(animated: true, completion: self?.completion)
        }
    }
}

Откройте встречу с помощью собственного flow delegate:

let delegate = CustomFlowDelegate()
let rtkUI = RealtimeKitUI(
    meetingInfo: RtkMeetingInfo(
        authToken: "<auth-token>",
        enableAudio: true,
        enableVideo: true
    ),
    flowDelegate: delegate
)

let controller = rtkUI.startMeeting {
    self.dismiss(animated: true)
}
controller.modalPresentationStyle = .fullScreen
present(controller, animated: true)

Отдельные компоненты, доступные для пользовательских экранов встречи:

Компонент Описание
RtkMeetingHeaderView Заголовок с названием встречи, количеством участников, часами и индикатором записи
RtkMeetingControlBar Панель управления с кнопками микрофона, камеры и завершения встречи
RtkParticipantTileView Видеоплитка одного участника с запасным аватаром и плашкой с именем
RtkVideoView Представление для рендеринга необработанного видео
RtkAvatarView Аватар участника с инициалами
RtkNameTag Имя участника со значком статуса микрофона
RtkAudioButtonControlBar Отдельная кнопка переключения микрофона
RtkVideoButtonControlBar Отдельная кнопка переключения камеры
RtkEndMeetingControlBarButton Кнопка завершения или выхода из встречи
RtkEventSelfListener Наблюдатель за состоянием аудио и видео, удалением, переподключением и разрешениями

В следующем примере создаётся пользовательский экран встречи с использованием отдельных представлений UI Kit в XML-разметке с activate(meeting) шаблон:

activity_custom_meeting.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#000000">

    <com.cloudflare.realtimekit.ui.view.headers.RtkMeetingHeaderView
        android:id="@+id/headerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <com.cloudflare.realtimekit.ui.view.grid.RtkGridView
        android:id="@+id/gridView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <com.cloudflare.realtimekit.ui.view.controlbars.RtkMeetingControlBarView
        android:id="@+id/controlBarView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
CustomMeetingActivity.kt
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.cloudflare.realtimekit.RealtimeKitClient
import com.cloudflare.realtimekit.RtkMeetingRoomEventListener
import com.cloudflare.realtimekit.models.RtkMeetingInfo
import com.cloudflare.realtimekit.ui.RealtimeKitUIBuilder
import com.cloudflare.realtimekit.ui.RealtimeKitUIInfo
import com.cloudflare.realtimekit.ui.view.controlbars.RtkMeetingControlBarView
import com.cloudflare.realtimekit.ui.view.grid.RtkGridView
import com.cloudflare.realtimekit.ui.view.headers.RtkMeetingHeaderView

class CustomMeetingActivity : AppCompatActivity() {
    private lateinit var headerView: RtkMeetingHeaderView
    private lateinit var gridView: RtkGridView
    private lateinit var controlBarView: RtkMeetingControlBarView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_custom_meeting)

        headerView = findViewById(R.id.headerView)
        gridView = findViewById(R.id.gridView)
        controlBarView = findViewById(R.id.controlBarView)

        val meetingInfo = RtkMeetingInfo(authToken = "<auth-token>")
        val uiKitInfo = RealtimeKitUIInfo(
            activity = this,
            rtkMeetingInfo = meetingInfo,
        )
        val rtkUI = RealtimeKitUIBuilder.build(uiKitInfo)
        val meeting: RealtimeKitClient = rtkUI.meeting

        // Listen for meeting lifecycle events
        meeting.addMeetingRoomEventListener(object : RtkMeetingRoomEventListener {
            override fun onMeetingRoomJoinCompleted(meeting: RealtimeKitClient) {
                runOnUiThread {
                    // Activate all components once the meeting is joined
                    headerView.activate(meeting)
                    gridView.activate(meeting)
                    controlBarView.activate(meeting)
                }
            }

            override fun onMeetingEnded() {
                finish()
            }

            override fun onMeetingRoomLeaveCompleted() {
                finish()
            }
        })
    }
}

Отдельные компоненты, доступные для пользовательских макетов:

Компонент Активируйте с помощью Описание
RtkGridView RealtimeKitClient Видеосетка участников с пагинацией
RtkMeetingHeaderView RealtimeKitClient Заголовок с названием, количеством участников, часами и индикатором записи
RtkMeetingControlBarView RealtimeKitClient Панель управления с кнопками микрофона, камеры, «Ещё» и выхода
RtkParticipantTileView RtkMeetingParticipant Видеоплитка одного участника с аватаром и плашкой с именем
RtkMicToggleButton RealtimeKitClient Отдельный переключатель микрофона
RtkCameraToggleButton RealtimeKitClient Отдельный переключатель камеры
RtkLeaveButton RealtimeKitClient Кнопка выхода/завершения встречи
RtkAvatarView RtkMeetingParticipant Аватар участника
RtkNameTagView RtkMeetingParticipant Name Tag с индикатором звука
RtkJoinButton RealtimeKitClient Кнопка присоединения к встрече для экранов настройки

В следующем примере используется RtkUIProvider и отдельных компонентов с рендерингом экранов на основе состояния, аналогично паттерну React для веба:

CustomMeeting.tsx
import React, { useContext, useEffect } from "react";
import { View, Text } from "react-native";
import {
	useRealtimeKitClient,
	RealtimeKitProvider,
} from "@cloudflare/realtimekit-react-native";
import {
	RtkUIProvider,
	RtkUIContext,
	RtkHeader,
	RtkGrid,
	RtkControlbar,
	RtkSidebar,
	RtkDialogManager,
	RtkSetupScreen,
	RtkWaitingScreen,
	RtkEndedScreen,
} from "@cloudflare/realtimekit-react-native-ui";

function MeetingScreens({ meeting }) {
	const { storeStates, setStates } = useContext(RtkUIContext);
	const currentState = storeStates.meeting;

	useEffect(() => {
		// Listen for meeting events and drive state transitions
		const onRoomJoined = () => {
			setStates({ ...storeStates, meeting: "joined" });
		};
		const onRoomLeft = ({ state }) => {
			if (state === "ended" || state === "left") {
				setStates({ ...storeStates, meeting: "ended" });
			}
		};
		meeting.self.addListener("roomJoined", onRoomJoined);
		meeting.self.addListener("roomLeft", onRoomLeft);
		return () => {
			meeting.self.removeListener("roomJoined", onRoomJoined);
			meeting.self.removeListener("roomLeft", onRoomLeft);
		};
	}, [meeting]);

	if (currentState === "setup") {
		return <RtkSetupScreen meeting={meeting} />;
	}

	if (currentState === "waiting") {
		return <RtkWaitingScreen />;
	}

	if (currentState === "joined") {
		return (
			<View style={{ flex: 1 }}>
				<RtkHeader meeting={meeting} />
				<View style={{ flex: 1 }}>
					<RtkGrid meeting={meeting} />
					{storeStates.activeSidebar && <RtkSidebar meeting={meeting} />}
				</View>
				<RtkControlbar meeting={meeting} />
				<RtkDialogManager meeting={meeting} />
			</View>
		);
	}

	if (currentState === "ended") {
		return <RtkEndedScreen meeting={meeting} />;
	}

	return <Text>Loading...</Text>;
}

function CustomMeeting({ authToken }) {
	const [meet, initMeeting] = useRealtimeKitClient();
	const { setStates, storeStates } = useContext(RtkUIContext);

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

	useEffect(() => {
		if (meet) {
			setStates({ ...storeStates, meeting: "setup" });
		}
	}, [meet]);

	if (!meet) return <Text>Initializing...</Text>;

	return (
		<RealtimeKitProvider value={meet}>
			<MeetingScreens meeting={meet} />
		</RealtimeKitProvider>
	);
}

export default function App() {
	return (
		<RtkUIProvider>
			<CustomMeeting authToken="<auth-token>" />
		</RtkUIProvider>
	);
}

Разделение первого уровня для RtkMeeting включает следующие компоненты:

Компонент Обязательный проп Описание
RtkHeader meeting Заголовок с названием встречи, количеством участников, часами и индикатором записи
RtkGrid meeting Видеосетка участников с пагинацией
RtkControlbar meeting Панель управления с переключателями микрофона, камеры, демонстрации экрана и других функций
RtkSidebar meeting Боковая панель с панелями чата, опросов, участников и плагинов
RtkDialogManager meeting Управляет всеми диалоговыми окнами (настройки, подтверждение выхода, выход на сцену)
RtkSetupScreen meeting Экран перед подключением с предпросмотром видео и выбором устройств
RtkWaitingScreen , Экран зала ожидания
RtkEndedScreen meeting Экран завершения встречи
RtkMicToggle meeting Отдельная кнопка переключения микрофона
RtkCameraToggle meeting Отдельная кнопка переключения камеры
RtkLeaveButton , Кнопка выхода/завершения встречи
RtkParticipantTile meeting, participant Видеоплитка одного участника
RtkNotifications meeting Toast-уведомления о событиях присоединения, выхода и чата