From 52edd5b5dbe2fc2e62756c5ec2dede4cde09aa3f Mon Sep 17 00:00:00 2001 From: mostafakamar2308 Date: Mon, 16 Dec 2024 17:45:02 +0200 Subject: [PATCH] feat(luna): changed calls component to handle stream organization --- .../src/components/Call/FocusedStream.tsx | 2 +- .../luna/src/components/Call/InCall/Call.tsx | 13 +- .../Call/InCall/CallWithChat.stories.tsx | 165 +++++++++--------- .../Call/InCall/CallWithoutChat.stories.tsx | 165 +++++++++--------- .../src/components/Call/InCallStreams.tsx | 38 ++-- packages/luna/src/components/Call/types.ts | 4 - packages/luna/src/internal/hooks/stream.ts | 18 +- packages/luna/src/internal/utils/stream.ts | 1 - packages/luna/src/lib/stream.ts | 63 +++++++ packages/luna/src/locales/ar-eg.json | 2 +- 10 files changed, 259 insertions(+), 212 deletions(-) create mode 100644 packages/luna/src/lib/stream.ts diff --git a/packages/luna/src/components/Call/FocusedStream.tsx b/packages/luna/src/components/Call/FocusedStream.tsx index 53443936f..3b8731344 100644 --- a/packages/luna/src/components/Call/FocusedStream.tsx +++ b/packages/luna/src/components/Call/FocusedStream.tsx @@ -50,7 +50,7 @@ export const FocusedStream: React.FC<{ autoPlay className={cn( "tw-w-full tw-aspect-video tw-absolute tw-top-0", - !stream.camera && !stream.cast && "tw-opacity-0" + !stream.stream || (!stream.camera && !stream.cast && "tw-opacity-0") )} muted={streamMuted} playsInline diff --git a/packages/luna/src/components/Call/InCall/Call.tsx b/packages/luna/src/components/Call/InCall/Call.tsx index 2787cae02..686b2e8c3 100644 --- a/packages/luna/src/components/Call/InCall/Call.tsx +++ b/packages/luna/src/components/Call/InCall/Call.tsx @@ -10,19 +10,8 @@ import { CallBar } from "@/components/Call/CallBar"; import { InCallStreams } from "@/components/Call/InCallStreams"; import { StreamInfo } from "@/components/Call/types"; -/** - * @todo should accept list of streams - * - user alone => should be the main stream - * - two users => the other user is the focused stream. - * - two users with cast => cast is the focused stream. - * - one user with casting => cast is the focused stream. - * - two users with two streams => the other user cast should be focused. - */ type Props = { - streams: { - focused: StreamInfo; - unfocused: StreamInfo[]; - }; + streams: StreamInfo[]; currentUserId: number; chat: { enabled: boolean; toggle: Void }; fullScreen: { diff --git a/packages/luna/src/components/Call/InCall/CallWithChat.stories.tsx b/packages/luna/src/components/Call/InCall/CallWithChat.stories.tsx index f2b07e350..396dc65f6 100644 --- a/packages/luna/src/components/Call/InCall/CallWithChat.stories.tsx +++ b/packages/luna/src/components/Call/InCall/CallWithChat.stories.tsx @@ -23,6 +23,8 @@ const meta: Meta = { ], }; +const CURRENT_USER_ID = 5; + export const AloneWithCamera: StoryObj = { args: { chat: { @@ -52,8 +54,10 @@ export const AloneWithCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const stream = useCreateStream("focused", true); - return ; + const stream = useCreateStream(true, CURRENT_USER_ID); + return ( + + ); }, }; @@ -87,8 +91,10 @@ export const Alert: StoryObj = { alert: faker.lorem.words(3), }, render(props) { - const stream = useCreateStream("focused", true); - return ; + const stream = useCreateStream(true, CURRENT_USER_ID); + return ( + + ); }, }; @@ -121,8 +127,10 @@ export const AloneWithoutCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const stream = useCreateStream("focused", false); - return ; + const stream = useCreateStream(false, CURRENT_USER_ID); + return ( + + ); }, }; @@ -155,9 +163,12 @@ export const FocusedWithUnfocusedWithoutCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const s1 = useCreateStream("focused", true); - const s2 = useCreateStream("unfocused", false); - return ; + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(false); + + return ( + + ); }, }; @@ -190,9 +201,11 @@ export const FocusedWithoutUnfocusedWithCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const s1 = useCreateStream("focused", false); - const s2 = useCreateStream("unfocused", true); - return ; + const s1 = useCreateStream(false, CURRENT_USER_ID); + const s2 = useCreateStream(true); + return ( + + ); }, }; @@ -225,10 +238,12 @@ export const FullRoomWithoutCameras: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const s1 = useCreateStream("focused", false); - const s2 = useCreateStream("unfocused", false); + const s1 = useCreateStream(false, CURRENT_USER_ID); + const s2 = useCreateStream(false); if (!s1 || !s2) return
; - return ; + return ( + + ); }, }; @@ -263,11 +278,17 @@ export const FullRoomWithCastWithCameras: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const cast = useCreateStream("focused", true); - const s1 = useCreateStream("unfocused", true); - const s2 = useCreateStream("unfocused", true); + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(true); if (!s1 || !s2 || !cast) return
; - return ; + return ( + + ); }, }; @@ -300,11 +321,17 @@ export const FullRoomWithCastWithoutCameras: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const cast = useCreateStream("focused", true); - const s1 = useCreateStream("unfocused", false); - const s2 = useCreateStream("unfocused", false); + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const s1 = useCreateStream(false, CURRENT_USER_ID); + const s2 = useCreateStream(false); if (!s1 || !s2 || !cast) return
; - return ; + return ( + + ); }, }; @@ -337,15 +364,20 @@ export const FullRoomWithCastWithCameraWithoutCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const cast = useCreateStream("focused", true); - const s1 = useCreateStream("unfocused", true); - const s2 = useCreateStream("unfocused", false); - return ; + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(false); + return ( + + ); }, }; -// Animation Testing -export const NewUserEntering: StoryObj = { +export const FullRoomWithFullCast: StoryObj = { args: { chat: { enabled: true, @@ -374,36 +406,22 @@ export const NewUserEntering: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const initialStream = useCreateStream("focused", false); - const unfocusedStream = useCreateStream("unfocused", false); - const [focused, setFocused] = useState(initialStream); - const [unfocused, setUnfocused] = useState([]); - - useEffect(() => { - setFocused(initialStream); - }, [initialStream]); - - useEffect(() => { - const timeout = setTimeout(() => { - setUnfocused((prev) => [...prev, unfocusedStream]); - }, 5_000); - - return () => clearTimeout(timeout); - }, [unfocusedStream]); - + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const cast2 = useCreateStream(true, 8, true); + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(false, 8); return ( ); }, }; -export const NewUserEnteringThenCasting: StoryObj = { +// Animation Testing +export const NewUserEntering: StoryObj = { args: { chat: { enabled: true, @@ -432,50 +450,27 @@ export const NewUserEnteringThenCasting: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const initialStream = useCreateStream("focused", false); - const unfocusedStream = useCreateStream("unfocused", false); - const unfocusedStream2 = useCreateStream("unfocused", false); - const cast = useCreateStream("focused", false); - const [focused, setFocused] = useState(initialStream); - const [unfocused, setUnfocused] = useState([]); + const initialStream = useCreateStream(false, CURRENT_USER_ID); + const unfocusedStream = useCreateStream(false); - useEffect(() => { - setFocused(initialStream); - }, [initialStream]); - - useEffect(() => { - const timeout = setTimeout(() => { - setUnfocused((prev) => [...prev, unfocusedStream]); - }, 5_000); - - return () => clearTimeout(timeout); - }, [unfocusedStream]); + const [streams, setStreams] = useState([]); useEffect(() => { - const timeout = setTimeout(() => { - setUnfocused((prev) => [...prev, unfocusedStream2]); - }, 10_000); - - return () => clearTimeout(timeout); - }, [unfocusedStream2]); + setStreams([initialStream]); + //eslint-disable-next-line + }, []); useEffect(() => { const timeout = setTimeout(() => { - setFocused(cast); - setUnfocused([initialStream, unfocusedStream, unfocusedStream2]); - }, 15_000); + setStreams((prev) => [...prev, unfocusedStream]); + }, 5_000); return () => clearTimeout(timeout); - }, [cast, initialStream, unfocusedStream, unfocusedStream2]); + //eslint-disable-next-line + }, []); return ( - + ); }, }; diff --git a/packages/luna/src/components/Call/InCall/CallWithoutChat.stories.tsx b/packages/luna/src/components/Call/InCall/CallWithoutChat.stories.tsx index 366f1bbd2..81abf25fb 100644 --- a/packages/luna/src/components/Call/InCall/CallWithoutChat.stories.tsx +++ b/packages/luna/src/components/Call/InCall/CallWithoutChat.stories.tsx @@ -23,6 +23,8 @@ const meta: Meta = { ], }; +const CURRENT_USER_ID = 5; + export const AloneWithCamera: StoryObj = { args: { chat: { @@ -52,8 +54,10 @@ export const AloneWithCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const stream = useCreateStream("focused", true); - return ; + const stream = useCreateStream(true, CURRENT_USER_ID); + return ( + + ); }, }; @@ -87,8 +91,10 @@ export const Alert: StoryObj = { alert: faker.lorem.words(3), }, render(props) { - const stream = useCreateStream("focused", true); - return ; + const stream = useCreateStream(true, CURRENT_USER_ID); + return ( + + ); }, }; @@ -121,8 +127,10 @@ export const AloneWithoutCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const stream = useCreateStream("focused", false); - return ; + const stream = useCreateStream(false, CURRENT_USER_ID); + return ( + + ); }, }; @@ -155,9 +163,12 @@ export const FocusedWithUnfocusedWithoutCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const s1 = useCreateStream("focused", true); - const s2 = useCreateStream("unfocused", false); - return ; + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(false); + + return ( + + ); }, }; @@ -190,9 +201,11 @@ export const FocusedWithoutUnfocusedWithCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const s1 = useCreateStream("focused", false); - const s2 = useCreateStream("unfocused", true); - return ; + const s1 = useCreateStream(false, CURRENT_USER_ID); + const s2 = useCreateStream(true); + return ( + + ); }, }; @@ -225,10 +238,12 @@ export const FullRoomWithoutCameras: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const s1 = useCreateStream("focused", false); - const s2 = useCreateStream("unfocused", false); + const s1 = useCreateStream(false, CURRENT_USER_ID); + const s2 = useCreateStream(false); if (!s1 || !s2) return
; - return ; + return ( + + ); }, }; @@ -263,11 +278,17 @@ export const FullRoomWithCastWithCameras: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const cast = useCreateStream("focused", true); - const s1 = useCreateStream("unfocused", true); - const s2 = useCreateStream("unfocused", true); + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(true); if (!s1 || !s2 || !cast) return
; - return ; + return ( + + ); }, }; @@ -300,11 +321,17 @@ export const FullRoomWithCastWithoutCameras: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const cast = useCreateStream("focused", true); - const s1 = useCreateStream("unfocused", false); - const s2 = useCreateStream("unfocused", false); + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const s1 = useCreateStream(false, CURRENT_USER_ID); + const s2 = useCreateStream(false); if (!s1 || !s2 || !cast) return
; - return ; + return ( + + ); }, }; @@ -337,15 +364,20 @@ export const FullRoomWithCastWithCameraWithoutCamera: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const cast = useCreateStream("focused", true); - const s1 = useCreateStream("unfocused", true); - const s2 = useCreateStream("unfocused", false); - return ; + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(false); + return ( + + ); }, }; -// Animation Testing -export const NewUserEntering: StoryObj = { +export const FullRoomWithFullCast: StoryObj = { args: { chat: { enabled: false, @@ -374,36 +406,22 @@ export const NewUserEntering: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const initialStream = useCreateStream("focused", false); - const unfocusedStream = useCreateStream("unfocused", false); - const [focused, setFocused] = useState(initialStream); - const [unfocused, setUnfocused] = useState([]); - - useEffect(() => { - setFocused(initialStream); - }, [initialStream]); - - useEffect(() => { - const timeout = setTimeout(() => { - setUnfocused((prev) => [...prev, unfocusedStream]); - }, 5_000); - - return () => clearTimeout(timeout); - }, [unfocusedStream]); - + const cast = useCreateStream(true, CURRENT_USER_ID, true); + const cast2 = useCreateStream(true, 8, true); + const s1 = useCreateStream(true, CURRENT_USER_ID); + const s2 = useCreateStream(false, 8); return ( ); }, }; -export const NewUserEnteringThenCasting: StoryObj = { +// Animation Testing +export const NewUserEntering: StoryObj = { args: { chat: { enabled: false, @@ -432,50 +450,27 @@ export const NewUserEnteringThenCasting: StoryObj = { chatPanel:
This is a Message Component
, }, render(props) { - const initialStream = useCreateStream("focused", false); - const unfocusedStream = useCreateStream("unfocused", false); - const unfocusedStream2 = useCreateStream("unfocused", false); - const cast = useCreateStream("focused", false); - const [focused, setFocused] = useState(initialStream); - const [unfocused, setUnfocused] = useState([]); + const initialStream = useCreateStream(false, CURRENT_USER_ID); + const unfocusedStream = useCreateStream(false); - useEffect(() => { - setFocused(initialStream); - }, [initialStream]); - - useEffect(() => { - const timeout = setTimeout(() => { - setUnfocused((prev) => [...prev, unfocusedStream]); - }, 5_000); - - return () => clearTimeout(timeout); - }, [unfocusedStream]); + const [streams, setStreams] = useState([]); useEffect(() => { - const timeout = setTimeout(() => { - setUnfocused((prev) => [...prev, unfocusedStream2]); - }, 10_000); - - return () => clearTimeout(timeout); - }, [unfocusedStream2]); + setStreams([initialStream]); + //eslint-disable-next-line + }, []); useEffect(() => { const timeout = setTimeout(() => { - setFocused(cast); - setUnfocused([initialStream, unfocusedStream, unfocusedStream2]); - }, 15_000); + setStreams((prev) => [...prev, unfocusedStream]); + }, 5_000); return () => clearTimeout(timeout); - }, [cast, initialStream, unfocusedStream, unfocusedStream2]); + //eslint-disable-next-line + }, []); return ( - + ); }, }; diff --git a/packages/luna/src/components/Call/InCallStreams.tsx b/packages/luna/src/components/Call/InCallStreams.tsx index 8e43ba593..4a798f804 100644 --- a/packages/luna/src/components/Call/InCallStreams.tsx +++ b/packages/luna/src/components/Call/InCallStreams.tsx @@ -1,18 +1,16 @@ -import React, { useRef } from "react"; +import React, { useMemo, useRef } from "react"; import cn from "classnames"; import { FocusedStream } from "@/components/Call/FocusedStream"; import { UnFocusedStream } from "@/components/Call/UnFocusedStream"; import { StreamInfo } from "@/components/Call/types"; import { Void } from "@litespace/types"; import { MovableMedia } from "../MovableMedia"; +import { streamsOrganizer } from "@/lib/stream"; export const InCallStreams: React.FC<{ alert?: string; currentUserId: number; - streams: { - focused: StreamInfo; - unfocused: StreamInfo[]; - }; + streams: StreamInfo[]; /** * Whether the chat panel is enabled or not. * @default false @@ -28,6 +26,14 @@ export const InCallStreams: React.FC<{ }; }> = ({ alert, streams, chat, timer, fullScreen, currentUserId }) => { const containerRef = useRef(null); + const newStreams = useMemo( + () => streamsOrganizer(streams, currentUserId), + [streams, currentUserId] + ); + + // SHOULD NEVER HAPPEN + if (!newStreams.focused) return null; + return (
- {streams.unfocused.map((stream, index) => ( - - - - ))} + {newStreams.unfocused.map((stream, index) => + stream ? ( + + + + ) : null + )}
); diff --git a/packages/luna/src/components/Call/types.ts b/packages/luna/src/components/Call/types.ts index a5fc6b126..6fbbc81ae 100644 --- a/packages/luna/src/components/Call/types.ts +++ b/packages/luna/src/components/Call/types.ts @@ -27,8 +27,4 @@ export type StreamInfo = { imageUrl: string | null; name: string | null; }; - /** - * type of component, who will have the largest portion of the screen - */ - type: "focused" | "unfocused"; }; diff --git a/packages/luna/src/internal/hooks/stream.ts b/packages/luna/src/internal/hooks/stream.ts index 44eeb130b..60cf5ce5c 100644 --- a/packages/luna/src/internal/hooks/stream.ts +++ b/packages/luna/src/internal/hooks/stream.ts @@ -3,8 +3,9 @@ import { createStreamInfo, getVideoMediaStream } from "@/internal/utils/stream"; import { faker } from "@faker-js/faker/locale/en"; export function useCreateStream( - type: "focused" | "unfocused", - camera?: boolean + camera?: boolean, + currentUserId?: number, + cast?: boolean ) { const [stream, setStream] = useState(null); useEffect(() => { @@ -13,15 +14,16 @@ export function useCreateStream( return createStreamInfo(stream, { user: { - id: faker.number.int({ - min: 1, - max: 1000, - }), + id: + currentUserId || + faker.number.int({ + min: 1, + max: 1000, + }), imageUrl: faker.image.urlPicsumPhotos({ width: 400, height: 400 }), name: faker.person.fullName(), }, - type: type, camera: camera || false, - cast: false, + cast: cast || false, }); } diff --git a/packages/luna/src/internal/utils/stream.ts b/packages/luna/src/internal/utils/stream.ts index b5a549724..1f67f3b0e 100644 --- a/packages/luna/src/internal/utils/stream.ts +++ b/packages/luna/src/internal/utils/stream.ts @@ -41,7 +41,6 @@ export function createStreamInfo( imageUrl: string | null; name: string | null; }; - type: "focused" | "unfocused"; camera: boolean; cast: boolean; } diff --git a/packages/luna/src/lib/stream.ts b/packages/luna/src/lib/stream.ts new file mode 100644 index 000000000..21ba08ff2 --- /dev/null +++ b/packages/luna/src/lib/stream.ts @@ -0,0 +1,63 @@ +import { StreamInfo } from "@/components/Call/types"; + +export function streamsOrganizer( + streams: StreamInfo[], + currentUserId: number +): { + focused: StreamInfo | undefined; + unfocused: (StreamInfo | undefined)[]; +} { + let currentUserStream: StreamInfo | undefined; + let currentUserCast: StreamInfo | undefined; + let otherUserStream: StreamInfo | undefined; + let otherUserCast: StreamInfo | undefined; + + /** + * assigning each stream to a unique variable + */ + streams.forEach((stream) => { + if (stream.user.id === currentUserId && stream.cast) { + currentUserCast = stream; + } + if (stream.user.id === currentUserId && !stream.cast) { + currentUserStream = stream; + } + + if (stream.user.id !== currentUserId && stream.cast) { + otherUserCast = stream; + } + if (stream.user.id !== currentUserId && !stream.cast) { + otherUserStream = stream; + } + }); + + /** + * Degree of Hirarchy -> Cast then streams and otherUser then currentUser + */ + if (otherUserCast) + return { + focused: otherUserCast, + unfocused: [currentUserCast, currentUserStream, otherUserStream].filter( + (stream) => stream !== undefined + ), + }; + + if (currentUserCast) + return { + focused: currentUserCast, + unfocused: [currentUserStream, otherUserStream].filter( + (stream) => stream !== undefined + ), + }; + + if (otherUserStream) + return { + focused: otherUserStream, + unfocused: [currentUserStream].filter((stream) => stream !== undefined), + }; + + return { + focused: currentUserStream, + unfocused: [], + }; +} diff --git a/packages/luna/src/locales/ar-eg.json b/packages/luna/src/locales/ar-eg.json index ef1041c63..161a46c52 100644 --- a/packages/luna/src/locales/ar-eg.json +++ b/packages/luna/src/locales/ar-eg.json @@ -692,4 +692,4 @@ "error.password.short": "رقم سري قصير", "error.password.long": "رقم سري طويل", "error.password.invalid": "يجب أن يحتوي علي أرقام، حروف ، رموز" -} \ No newline at end of file +}