INTEGRITY Dokumentace

Add Custom Header

V tomto průvodci se naučíme, jak do schůzky RealtimeKit přidat vlastní hlavičku.

RealtimeKit UI poskytuje RtkHeader komponenta pro výchozí hlavičku.

Pokud potřebujete další ovládací prvky, nahraďte RtkHeader s jednotlivými komponentami UI Kit a vlastními prvky.

Importujte potřebné komponenty:

import {
	RtkLogo,
	RtkRecordingIndicator,
	RtkLivestreamIndicator,
	RtkMeetingTitle,
	RtkGridPagination,
	RtkParticipantCount,
	RtkViewerCount,
	RtkClock,
} from "@cloudflare/realtimekit-ui";

Ve vašem RtkUIProvider z Vytváření vlastního UI, nahraďte:

<RtkHeader />

za:

<div
	style={{
		display: "flex",
		justifyContent: "space-between",
		backgroundColor: "black",
		color: "white",
	}}
>
	<div
		id="header-left"
		style={{ display: "flex", alignItems: "center", height: "48px" }}
	>
		<RtkLogo />
		<RtkRecordingIndicator />
		<RtkLivestreamIndicator />
	</div>
	<div
		id="header-center"
		style={{ display: "flex", alignItems: "center", height: "48px" }}
	>
		<RtkMeetingTitle />
	</div>
	<div
		id="header-right"
		style={{ display: "flex", alignItems: "center", height: "48px" }}
	>
		<RtkGridPagination />
		<RtkParticipantCount />
		<RtkViewerCount />
		<RtkClock />
		<button onClick={handleReportBugClick}>Report Bug</button>
	</div>
</div>

Definujte handler kliknutí:

const handleReportBugClick = () => {
	console.log("Report Bug Clicked");
};

Úplný příklad najdete v příklad vlastního UI a vlastní komponenta záhlaví.

RealtimeKit UI poskytuje rtk-header komponenta pro výchozí hlavičku.

Pokud potřebujete další ovládací prvky, nahraďte rtk-header s jednotlivými komponentami UI Kit a vlastními prvky. V renderJoinedScreen funkce z Vytváření vlastního UI, nahraďte:

<rtk-header style="display: flex; justify-content: space-between;"></rtk-header>

za:

<div
	style="display: flex; justify-content: space-between; align-items: center; height: 48px; padding: 0 12px; background-color: black; color: white;"
>
	<div style="display: flex; align-items: center; gap: 8px;">
		<rtk-logo></rtk-logo>
		<rtk-recording-indicator></rtk-recording-indicator>
		<rtk-livestream-indicator></rtk-livestream-indicator>
	</div>

	<div style="display: flex; align-items: center;">
		<rtk-meeting-title></rtk-meeting-title>
	</div>

	<div style="display: flex; align-items: center; gap: 8px;">
		<rtk-grid-pagination></rtk-grid-pagination>
		<rtk-participant-count></rtk-participant-count>
		<rtk-viewer-count></rtk-viewer-count>
		<rtk-clock></rtk-clock>
		<button id="report-bug-button" type="button">Report Bug</button>
	</div>
</div>

Zaregistrujte handler kliknutí po vykreslení:

document.querySelector("#report-bug-button").addEventListener("click", () => {
	console.log("Report Bug Clicked");
});

Úplný příklad najdete v příklad vlastního UI a vlastní komponenta záhlaví.

RealtimeKit UI poskytuje rtk-header komponenta pro výchozí hlavičku.

Pokud potřebujete další ovládací prvky, nahraďte rtk-header s jednotlivými komponentami UI Kit a vlastními prvky. Vytvořte vlastní komponentu header, která přímo využívá komponenty RealtimeKit pro Angular.

Vytvoření vlastní komponenty záhlaví

custom-header.component.ts
import { Component, AfterViewInit } from "@angular/core";

@Component({
	selector: "app-custom-header",
	template: `
		<div class="custom-header">
			<div class="header-left">
				<rtk-logo></rtk-logo>
				<rtk-recording-indicator></rtk-recording-indicator>
				<rtk-livestream-indicator></rtk-livestream-indicator>
			</div>

			<div class="header-center">
				<rtk-meeting-title></rtk-meeting-title>
			</div>

			<div class="header-right">
				<rtk-grid-pagination></rtk-grid-pagination>
				<rtk-participant-count></rtk-participant-count>
				<rtk-viewer-count></rtk-viewer-count>
				<rtk-clock></rtk-clock>
				<button
					type="button"
					class="report-bug-button"
					(click)="onReportBugClick()"
				>
					Report Bug
				</button>
			</div>
		</div>
	`,
	styles: [
		`
			.custom-header {
				display: flex;
				justify-content: space-between;
				align-items: center;
				height: 48px;
				padding: 0 12px;
				background-color: black;
				color: white;
			}

			.header-left,
			.header-right {
				display: flex;
				align-items: center;
				gap: 8px;
			}

			.header-center {
				display: flex;
				align-items: center;
			}

			.report-bug-button {
				background: none;
				border: 1px solid white;
				color: white;
				padding: 4px 8px;
				border-radius: 4px;
				cursor: pointer;
				font-size: 12px;
			}

			.report-bug-button:hover {
				background-color: rgba(255, 255, 255, 0.1);
			}
		`,
	],
})
export class CustomHeaderComponent implements AfterViewInit {
	ngAfterViewInit() {
		console.log("Custom header initialized");
	}

	onReportBugClick() {
		console.log("Report Bug Clicked");
		// Add your custom logic here
		// For example: open a modal, navigate to a form, etc.
	}
}

Použití ve vaší komponentě schůzky

V šabloně hlavní komponenty schůzky nahraďte:

<rtk-header style="display: flex; justify-content: space-between;"></rtk-header>

za:

<app-custom-header></app-custom-header>

Kompletní příklad komponenty schůzky

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

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

			<!-- Other meeting UI components -->
			<rtk-grid></rtk-grid>
			<rtk-sidebar></rtk-sidebar>
		</rtk-meeting>
	`,
})
export class MeetingComponent implements OnInit, OnDestroy {
	@ViewChild("meetingComponent", { static: true }) meetingElement!: ElementRef;

	meeting: any;
	private authToken = "<participant_auth_token>";

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

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

		const meetingComponent = this.meetingElement.nativeElement;
		meetingComponent.showSetupScreen = true;
		meetingComponent.meeting = this.meeting;
	}

	ngOnDestroy() {
		// Cleanup logic
	}
}

Konfigurace modulu

Nezapomeňte deklarovat svou vlastní komponentu header ve svém modulu Angular:

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

import { AppComponent } from "./app.component";
import { MeetingComponent } from "./meeting.component";
import { CustomHeaderComponent } from "./custom-header.component";

@NgModule({
	declarations: [AppComponent, MeetingComponent, CustomHeaderComponent],
	imports: [BrowserModule],
	providers: [],
	bootstrap: [AppComponent],
	schemas: [CUSTOM_ELEMENTS_SCHEMA], // Required for RTK web components
})
export class AppModule {}

Tento přístup vám dává úplnou kontrolu nad rozvržením hlavičky, přičemž zachovává komponentovou architekturu Angularu a způsob zpracování událostí.

RealtimeKit UI poskytuje RtkMeetingHeaderView komponenta pro výchozí hlavičku.

Pokud potřebujete další ovládací prvky, nahraďte RtkMeetingHeaderView s jednotlivými komponentami UI Kit a vlastními prvky.

Importujte potřebné komponenty:

import RealtimeKitUI

Vytvoření vlastního zobrazení záhlaví

Vytvoření vlastního zobrazení záhlaví, které přímo používá komponenty RealtimeKit iOS:

CustomHeaderView.swift
import UIKit
import RealtimeKit
import RealtimeKitUI

class CustomHeaderView: UIView {
    private let meeting: RealtimeKitClient

    private lazy var titleLabel: RtkMeetingTitleLabel = {
        return RtkMeetingTitleLabel(meeting: meeting)
    }()

    private lazy var participantCountView: RtkParticipantCountView = {
        return RtkParticipantCountView(meeting: meeting)
    }()

    private lazy var clockView: RtkClockView = {
        return RtkClockView(meeting: meeting)
    }()

    private lazy var recordingView: RtkRecordingView = {
        return RtkRecordingView(meeting: meeting)
    }()

    private let reportBugButton = UIButton(type: .system)

    init(meeting: RealtimeKitClient) {
        self.meeting = meeting
        super.init(frame: .zero)
        setupUI()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupUI() {
        backgroundColor = .black

        // Configure report bug button
        reportBugButton.setTitle("Report Bug", for: .normal)
        reportBugButton.setTitleColor(.white, for: .normal)
        reportBugButton.layer.borderColor = UIColor.white.cgColor
        reportBugButton.layer.borderWidth = 1
        reportBugButton.layer.cornerRadius = 4
        reportBugButton.contentEdgeInsets = UIEdgeInsets(top: 4, left: 8, bottom: 4, right: 8)
        reportBugButton.addTarget(self, action: #selector(handleReportBugTap), for: .touchUpInside)

        // Create stack views for layout
        let leftStack = UIStackView(arrangedSubviews: [recordingView])
        leftStack.axis = .horizontal
        leftStack.spacing = 8
        leftStack.alignment = .center

        let centerStack = UIStackView(arrangedSubviews: [titleLabel])
        centerStack.axis = .horizontal
        centerStack.alignment = .center

        let rightStack = UIStackView(arrangedSubviews: [
            participantCountView,
            clockView,
            reportBugButton
        ])
        rightStack.axis = .horizontal
        rightStack.spacing = 8
        rightStack.alignment = .center

        let mainStack = UIStackView(arrangedSubviews: [leftStack, centerStack, rightStack])
        mainStack.axis = .horizontal
        mainStack.distribution = .equalSpacing
        mainStack.alignment = .center
        mainStack.translatesAutoresizingMaskIntoConstraints = false

        addSubview(mainStack)

        NSLayoutConstraint.activate([
            mainStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
            mainStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
            mainStack.topAnchor.constraint(equalTo: topAnchor),
            mainStack.bottomAnchor.constraint(equalTo: bottomAnchor),
            heightAnchor.constraint(equalToConstant: 48)
        ])
    }

    @objc private func handleReportBugTap() {
        print("Report Bug Tapped")
        // Add your custom logic here
    }
}

Použití ve vašem view controlleru schůzky

Ve vašem MeetingViewController, nahraďte výchozí hlavičku svou vlastní hlavičkou:

MeetingViewController.swift
import UIKit
import RealtimeKit
import RealtimeKitUI

class MeetingViewController: UIViewController {
    private var meeting: RealtimeKitClient?
    private var customHeader: CustomHeaderView?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupMeeting()
    }

    private func setupMeeting() {
        Task {
            do {
                let client = try await RealtimeKitClient.init(authToken: "<PARTICIPANT_AUTH_TOKEN>")
                self.meeting = client
                await MainActor.run {
                    setupCustomHeader(meeting: client)
                }
            } catch {
                print("Failed to initialize meeting: \(error)")
            }
        }
    }

    private func setupCustomHeader(meeting: RealtimeKitClient) {
        let header = CustomHeaderView(meeting: meeting)
        self.customHeader = header

        view.addSubview(header)
        header.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            header.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            header.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            header.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
}

RealtimeKit UI poskytuje RtkMeetingHeaderView komponenta pro výchozí hlavičku.

Pokud potřebujete další ovládací prvky, nahraďte RtkMeetingHeaderView s jednotlivými komponentami UI Kit a vlastními prvky.

Importujte potřebné komponenty:

import com.cloudflare.realtimekit.ui.view.headers.RtkMeetingHeaderView
import com.cloudflare.realtimekit.ui.view.RtkMeetingTitleView
import com.cloudflare.realtimekit.ui.view.RtkParticipantCountView
import com.cloudflare.realtimekit.ui.view.RtkClockView
import com.cloudflare.realtimekit.ui.view.RtkRecordingIndicator
import com.cloudflare.realtimekit.ui.view.RtkLivestreamIndicator
import com.cloudflare.realtimekit.ui.view.RtkGridPaginatorView

Vytvoření vlastního layoutu záhlaví

Vytvoření souboru XML layoutu pro vlastní záhlaví:

layout/custom_header.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="48dp"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:paddingHorizontal="12dp"
    android:background="@android:color/black">

    <!-- Left section -->
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="horizontal"
        android:gravity="start|center_vertical">

        <com.cloudflare.realtimekit.ui.view.RtkRecordingIndicator
            android:id="@+id/recordingIndicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <com.cloudflare.realtimekit.ui.view.RtkLivestreamIndicator
            android:id="@+id/livestreamIndicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp" />
    </LinearLayout>

    <!-- Center section -->
    <com.cloudflare.realtimekit.ui.view.RtkMeetingTitleView
        android:id="@+id/meetingTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white" />

    <!-- Right section -->
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="horizontal"
        android:gravity="end|center_vertical">

        <com.cloudflare.realtimekit.ui.view.RtkGridPaginatorView
            android:id="@+id/gridPaginator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <com.cloudflare.realtimekit.ui.view.RtkParticipantCountView
            android:id="@+id/participantCount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp" />

        <com.cloudflare.realtimekit.ui.view.RtkClockView
            android:id="@+id/clock"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp" />

        <Button
            android:id="@+id/reportBugButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:text="Report Bug"
            android:textColor="@android:color/white"
            android:background="@android:color/transparent"
            style="?android:attr/borderlessButtonStyle" />
    </LinearLayout>
</LinearLayout>

Vytvoření vlastní třídy zobrazení záhlaví

CustomHeaderView.kt
package com.example.meeting

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.widget.Button
import android.widget.LinearLayout
import com.cloudflare.realtimekit.ui.view.RtkClockView
import com.cloudflare.realtimekit.ui.view.RtkMeetingTitleView
import com.cloudflare.realtimekit.ui.view.RtkParticipantCountView
import com.cloudflare.realtimekit.ui.view.RtkRecordingIndicator

class CustomHeaderView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {

    init {
        LayoutInflater.from(context).inflate(R.layout.custom_header, this, true)
        setupReportBugButton()
    }

    private fun setupReportBugButton() {
        findViewById<Button>(R.id.reportBugButton).setOnClickListener {
            Log.d("CustomHeader", "Report Bug Clicked")
            // Add your custom logic here
        }
    }
}

Použití ve vaší schůzkové Activity

Ve vašem MeetingActivity, nahraďte výchozí hlavičku svou vlastní hlavičkou:

MeetingActivity.kt
package com.example.meeting

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.cloudflare.realtimekit.RealtimeKitClient

class MeetingActivity : AppCompatActivity() {
    private lateinit var meeting: RealtimeKitClient
    private lateinit var customHeader: CustomHeaderView

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

        customHeader = findViewById(R.id.customHeader)
        initializeMeeting()
    }

    private fun initializeMeeting() {
        RealtimeKitClient.init(
            authToken = "<PARTICIPANT_AUTH_TOKEN>",
            onSuccess = { client ->
                meeting = client
                // Configure meeting UI
            },
            onError = { error ->
                Log.e("Meeting", "Failed to initialize: $error")
            }
        )
    }
}

RealtimeKit UI poskytuje RtkHeader komponenta pro výchozí hlavičku.

Pokud potřebujete další ovládací prvky, nahraďte RtkHeader s jednotlivými komponentami UI Kit a vlastními prvky.

Importujte potřebné komponenty:

import {
	RtkLogo,
	RtkRecordingIndicator,
	RtkLiveStreamIndicator,
	RtkMeetingTitle,
	RtkGridPagination,
	RtkParticipantCount,
	RtkClock,
} from "@cloudflare/realtimekit-react-native-ui";
import {
	useRealtimeKitMeeting,
	useRealtimeKitSelector,
} from "@cloudflare/realtimekit-react-native";

Vytvoření vlastní komponenty záhlaví

Vytvoření vlastní komponenty záhlaví, která přímo používá komponenty RealtimeKit React Native. Každá komponenta vyžaduje meeting instanci jako prop, kterou získáte pomocí useRealtimeKitMeeting hook. Komponenta čeká na roomJoined před vykreslením, aby bylo zajištěno, že je stav schůzky připravený:

CustomHeader.tsx
import React from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
import {
	RtkLogo,
	RtkRecordingIndicator,
	RtkLiveStreamIndicator,
	RtkMeetingTitle,
	RtkGridPagination,
	RtkParticipantCount,
	RtkClock,
} from "@cloudflare/realtimekit-react-native-ui";
import {
	useRealtimeKitMeeting,
	useRealtimeKitSelector,
} from "@cloudflare/realtimekit-react-native";

interface CustomHeaderProps {
	onReportBugPress?: () => void;
}

export function CustomHeader({ onReportBugPress }: CustomHeaderProps) {
	const { meeting } = useRealtimeKitMeeting();
	const { roomJoined } = useRealtimeKitSelector((state) => state.self);

	const handleReportBugPress = () => {
		console.log("Report Bug Pressed");
		onReportBugPress?.();
	};

	if (!roomJoined) {
		return null;
	}

	return (
		<View style={styles.container}>
			{/* Left section */}
			<View style={styles.leftSection}>
				<RtkLogo />
				<RtkRecordingIndicator meeting={meeting} />
				<RtkLiveStreamIndicator meeting={meeting} />
			</View>

			{/* Center section */}
			<View style={styles.centerSection}>
				<RtkMeetingTitle meeting={meeting} />
			</View>

			{/* Right section */}
			<View style={styles.rightSection}>
				<RtkGridPagination meeting={meeting} />
				<RtkParticipantCount meeting={meeting} />
				<RtkClock meeting={meeting} />
				<TouchableOpacity
					style={styles.reportBugButton}
					onPress={handleReportBugPress}
				>
					<Text style={styles.reportBugText}>Report Bug</Text>
				</TouchableOpacity>
			</View>
		</View>
	);
}

const styles = StyleSheet.create({
	container: {
		flexDirection: "row",
		justifyContent: "space-between",
		alignItems: "center",
		height: 48,
		paddingHorizontal: 12,
		backgroundColor: "black",
	},
	leftSection: {
		flexDirection: "row",
		alignItems: "center",
		gap: 8,
	},
	centerSection: {
		flexDirection: "row",
		alignItems: "center",
	},
	rightSection: {
		flexDirection: "row",
		alignItems: "center",
		gap: 8,
	},
	reportBugButton: {
		borderWidth: 1,
		borderColor: "white",
		borderRadius: 4,
		paddingHorizontal: 8,
		paddingVertical: 4,
	},
	reportBugText: {
		color: "white",
		fontSize: 12,
	},
});

Použití na vaší obrazovce schůzky

Na obrazovce schůzky nahraďte výchozí RtkHeader s vlastní komponentou header:

MeetingScreen.tsx
import React, { useEffect } from "react";
import { View, StyleSheet } from "react-native";
import {
	RealtimeKitProvider,
	useRealtimeKitClient,
} from "@cloudflare/realtimekit-react-native";
import {
	RtkGrid,
	RtkControlbar,
	RtkDialogManager,
} from "@cloudflare/realtimekit-react-native-ui";
import { CustomHeader } from "./CustomHeader";

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

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

	useEffect(() => {
		if (!meeting) return;
		meeting.join();
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [meeting]);

	if (!meeting) {
		return null;
	}

	return (
		<RealtimeKitProvider value={meeting}>
			<View style={styles.container}>
				{/* Custom header replaces RtkHeader */}
				<CustomHeader
					onReportBugPress={() => console.log("Report bug pressed")}
				/>

				{/* Meeting grid */}
				<View style={styles.gridContainer}>
					<RtkGrid meeting={meeting} />
				</View>

				{/* Control bar */}
				<RtkControlbar meeting={meeting} />
			</View>

			{/* Required components */}
			<RtkDialogManager meeting={meeting} />
		</RealtimeKitProvider>
	);
}

export function MeetingScreen() {
	return <MeetingContainer />;
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		backgroundColor: "black",
	},
	gridContainer: {
		flex: 1,
	},
});