Skip to content

Commit

Permalink
feat(rtc): add icons
Browse files Browse the repository at this point in the history
  • Loading branch information
crimx committed Mar 14, 2023
1 parent f841c76 commit bbb2516
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 2 deletions.
6 changes: 6 additions & 0 deletions packages/agora-rtc-react/src/components/MicControl.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.agora-rtc-mic-control {
background-color: transparent;
border: none;
outline: none;
cursor: pointer;
}
47 changes: 47 additions & 0 deletions packages/agora-rtc-react/src/components/MicControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { ButtonHTMLAttributes } from "react";

import { useEffect, useState } from "react";
import { useRTCClient } from "../hooks";
import { listen } from "../listen";
import { SVGMicrophone } from "./icons/SVGMicrophone";
import { SVGMicrophoneMute } from "./icons/SVGMicrophoneMute";

export interface MicControlProps extends ButtonHTMLAttributes<HTMLButtonElement> {
uid?: string | number;
micOn: boolean;
onMicChange: (micOn: boolean) => void;
/** 0~1 */
noise?: number;
}

export function MicControl({
noise,
uid,
micOn,
onMicChange,
className = "",
...props
}: MicControlProps) {
const [volumeLevel, setVolumeLevel] = useState(0);

const client = useRTCClient(true);

useEffect(() => {
if (uid != null && client) {
return listen(client, "volume-indicator", results => {
for (const volume of results) {
if (volume.uid === uid) {
setVolumeLevel(volume.level);
break;
}
}
});
}
}, [uid, client]);

return (
<button {...props} className={`agora-rtc-mic-control ${className}`}>
{micOn ? <SVGMicrophone volumeLevel={volumeLevel} noise={noise} /> : <SVGMicrophoneMute />}
</button>
);
}
51 changes: 51 additions & 0 deletions packages/agora-rtc-react/src/components/icons/SVGCamera.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { SVGProps } from "react";

import { memo } from "react";

export type SVGCameraProps = SVGProps<SVGSVGElement>;

export const SVGCamera = /* @__PURE__ */ memo<SVGCameraProps>(function SVGCamera(props) {
return (
<svg
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<circle
className="flat-icon-stroke-color"
cx="12"
cy="11"
r="7"
stroke="#5D6066"
strokeLinejoin="round"
strokeWidth="1.25"
></circle>
<circle
className="flat-icon-stroke-color"
cx="12"
cy="11"
r="3"
stroke="#5D6066"
strokeLinejoin="round"
strokeWidth="1.25"
></circle>
<circle
className="flat-icon-fill-color"
cx="14.625"
cy="6.625"
fill="#5D6066"
r=".625"
></circle>
<path
className="flat-icon-stroke-color"
d="M7 18.25a8.004 8.004 0 0 0 10 0"
stroke="#5D6066"
strokeLinejoin="round"
strokeWidth="1.25"
></path>
</svg>
);
});
35 changes: 35 additions & 0 deletions packages/agora-rtc-react/src/components/icons/SVGCameraMute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { SVGProps } from "react";

import { memo } from "react";

export type SVGCameraMuteProps = SVGProps<SVGSVGElement>;

export const SVGCameraMute = /* @__PURE__ */ memo<SVGCameraMuteProps>(function SVGCameraMute(
props,
) {
return (
<svg
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
className="flat-icon-stroke-color"
d="m5 5 14 14"
stroke="#5D6066"
strokeLinejoin="round"
strokeWidth="1.25"
></path>
<path
className="flat-icon-fill-color"
clipRule="evenodd"
d="m15.72 18.373.91.909a8.63 8.63 0 0 1-9.788-.364l-.232-.18.78-.976a7.38 7.38 0 0 0 8.33.61Zm2.434-2.87A7.625 7.625 0 0 0 7.497 4.846l.897.896a6.375 6.375 0 0 1 8.864 8.864l.896.897Zm-3.857 1.446.952.951A7.625 7.625 0 0 1 5.1 7.751l.951.952a6.375 6.375 0 0 0 8.246 8.246Zm.956-4.348a3.625 3.625 0 0 0-4.854-4.854l.964.964a2.378 2.378 0 0 1 2.926 2.926l.964.964Zm-.628-5.351a.625.625 0 1 0 0-1.25.625.625 0 0 0 0 1.25Z"
fill="#5D6066"
fillRule="evenodd"
></path>
</svg>
);
});
77 changes: 77 additions & 0 deletions packages/agora-rtc-react/src/components/icons/SVGMicrophone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { SVGProps } from "react";
import { useState, useEffect } from "react";

const vHeight = 14;
const vWidth = 8;
const vBaseX = 8;
const vBaseY = 4;

export interface SVGMicrophoneProps extends SVGProps<SVGSVGElement> {
/** 0~100 */
volumeLevel?: number;
/** 0~1 */
noise?: number;
}

export function SVGMicrophone({
volumeLevel: baseVolumeLevel = 0,
noise = 0.075,
...props
}: SVGMicrophoneProps) {
const [volumeLevel, setVolumeLevel] = useState(0);

useEffect(() => {
if (baseVolumeLevel && noise) {
const safeNoise = Math.max(0, Math.min(1, noise));
const ticket = setInterval(() => {
setVolumeLevel(
baseVolumeLevel + Math.random() * safeNoise * (Math.random() > 0.5 ? 1 : -1),
);
}, 50);
return () => clearInterval(ticket);
}
}, [baseVolumeLevel, noise]);

return (
<svg
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<defs>
<clipPath id="icon-mic-v-clip">
<rect height={vHeight} rx={vWidth / 2} width={vWidth} x={vBaseX} y={vBaseY} />
</clipPath>
</defs>
<path d="M0 0h24v24H0z" fill="#999CA3" opacity=".01" />
<rect
clipPath="url(#icon-mic-v-clip)"
fill="#fff"
height={vHeight}
width={vWidth}
x={vBaseX}
y={vBaseY}
/>
<path
d="M4 16.625h2v-1.25H4v1.25Zm6 4h4v-1.25h-4v1.25Zm8-4h2v-1.25h-2v1.25Zm-4 4A4.625 4.625 0 0 0 18.625 16h-1.25A3.375 3.375 0 0 1 14 19.375v1.25ZM5.375 16A4.625 4.625 0 0 0 10 20.625v-1.25A3.375 3.375 0 0 1 6.625 16h-1.25Z"
fill="#fff"
/>
<g clipPath="url(#icon-mic-v-clip)">
<rect
fill="#44AD00"
height={vHeight * 2}
style={{
transform: `translateY(${(1 - volumeLevel) * vHeight}px)`,
transition: "transform .1s",
}}
width={vWidth}
x={vBaseX}
y={vBaseY}
/>
</g>
</svg>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { SVGProps } from "react";

import { memo } from "react";

export type SVGMicrophoneProps = SVGProps<SVGSVGElement>;

export const SVGMicrophoneMute = /* @__PURE__ */ memo<SVGMicrophoneProps>(
function SVGMicrophoneMute(props) {
return (
<svg
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
className="flat-icon-stroke-color"
d="m5 5 14 14"
stroke="#5D6066"
strokeLinejoin="round"
strokeWidth="1.25"
></path>
<path
className="flat-icon-fill-color"
clipRule="evenodd"
d="M19.277 16.625H20v-1.25h-1.973l1.25 1.25Zm-3.239 2.065.89.89a4.602 4.602 0 0 1-2.716 1.04l-.212.005h-4a4.626 4.626 0 0 1-4.55-3.787l-.033-.213H4v-1.25h2c.345 0 .625.28.625.625a3.375 3.375 0 0 0 3.19 3.37l.185.005h4a3.36 3.36 0 0 0 2.038-.685Zm.587-4.717V8a4.625 4.625 0 0 0-8.5-2.526l.911.91a3.374 3.374 0 0 1 6.281.991H14v1.25h1.375v.75H14v1.25h1.375v.75h-1.348l1.25 1.25h.098v.098l1.25 1.25Zm-2.587 2.717.89.89A4.625 4.625 0 0 1 7.375 14v-3.973l1.25 1.25v.098h.098l1.25 1.25H8.625V14a3.375 3.375 0 0 0 5.413 2.69Z"
fill="#5D6066"
fillRule="evenodd"
></path>
</svg>
);
},
);
15 changes: 13 additions & 2 deletions packages/agora-rtc-react/src/hooks/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,21 @@ export function AgoraRTCProvider({ client, children }: PropsWithChildren<AgoraRT
return <AgoraRTCContext.Provider value={client}>{children}</AgoraRTCContext.Provider>;
}

export function useRTCClient() {
/**
* Get a Agora RTC client from context
*/
export function useRTCClient(optional?: false): IAgoraRTCClient;
/**
* Get a Agora RTC client from context
* @param optional do not throw error if client is not found
*/
export function useRTCClient(optional: true): IAgoraRTCClient | null;
export function useRTCClient(optional?: boolean) {
const client = useContext(AgoraRTCContext);
if (!client) {
throw new Error("should be wrapped in <AgoraRTCProvider value={client} />");
if (!optional) {
throw new Error("should be wrapped in <AgoraRTCProvider value={client} />");
}
}
return client;
}

0 comments on commit bbb2516

Please sign in to comment.