Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PB-3785, PB-3784] feature/Add prejoin modal and video component #16

Open
wants to merge 3 commits into
base: feature/PB-3783-create-meet-header-component
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion css/_reset.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ input[type="submit"],
input[type="reset"] {
-webkit-appearance: button;
}
input,
// input,
button,
textarea,
select {
Expand Down
Binary file added images/VideoCameraSlash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/premeetingscreen/MicrophoneSlash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion lang/main-es.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"signUp": "Registrarse"
},
"preMeeting": {
"newMeeting": "Nueva reunión"
"newMeeting": "Nueva reunión",
"enterYourName": "Introduce tu nombre",
"joinMeeting": "Unirse a la reunión",
"nameRequired": "Se requiere un nombre"
}
},
"addPeople": {
Expand Down
5 changes: 4 additions & 1 deletion lang/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"signUp": "Sign up"
},
"preMeeting": {
"newMeeting": "New Meeting"
"newMeeting": "New Meeting",
"enterYourName": "Enter your name",
"joinMeeting": "Join Meeting",
"nameRequired": "A name is required"
}
},
"addPeople": {
Expand Down
2,694 changes: 2,537 additions & 157 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@
"@babel/preset-env": "7.21.5",
"@babel/preset-react": "7.16.0",
"@jitsi/eslint-config": "4.1.10",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/react-hooks": "^8.0.1",
"@types/amplitude-js": "8.16.5",
"@types/audioworklet": "0.0.29",
"@types/dom-screen-wake-lock": "1.0.1",
Expand All @@ -168,6 +172,7 @@
"@types/zxcvbn": "4.4.1",
"@typescript-eslint/eslint-plugin": "5.59.5",
"@typescript-eslint/parser": "5.59.5",
"@vitest/coverage-v8": "^3.0.5",
"autoprefixer": "^10.4.20",
"babel-loader": "8.2.3",
"babel-plugin-optional-require": "0.3.1",
Expand All @@ -181,6 +186,7 @@
"eslint-plugin-react-native": "4.0.0",
"eslint-plugin-typescript-sort-keys": "2.3.0",
"jetifier": "1.6.4",
"jsdom": "^26.0.0",
"metro-react-native-babel-preset": "0.75.1",
"patch-package": "6.4.7",
"postcss": "^8.5.1",
Expand All @@ -193,6 +199,7 @@
"ts-loader": "9.4.2",
"typescript": "5.0.4",
"unorm": "1.6.0",
"vitest": "^3.0.5",
"webpack": "5.76.0",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "4.10.0",
Expand All @@ -219,7 +226,11 @@
"validate": "npm ls",
"tsc-test:web": "tsc --project tsconfig.web.json --listFilesOnly | grep -v node_modules | grep native",
"tsc-test:native": "tsc --project tsconfig.native.json --listFilesOnly | grep -v node_modules | grep web",
"start": "make dev"
"start": "make dev",
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"test:ci": "vitest run --coverage --reporter=junit"
},
"resolutions": {
"@types/react": "17.0.14",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { ReactNode, useMemo } from "react";
import React, { ReactNode, useMemo, useState } from "react";
import { connect } from "react-redux";
import { makeStyles } from "tss-react/mui";
import { translate } from "../../../../base/i18n/functions";
import { translate } from "../../../i18n/functions";

import { IReduxState } from "../../../../app/types";
import DeviceStatus from "../../../../prejoin/components/web/preview/DeviceStatus";
Expand All @@ -12,12 +12,15 @@ import { getConferenceName } from "../../../conference/functions";
import { PREMEETING_BUTTONS, THIRD_PARTY_PREJOIN_BUTTONS } from "../../../config/constants";
import { withPixelLineHeight } from "../../../styles/functions.web";

import { Avatar, Button, Header } from "@internxt/ui";
import { Button, Input, TransparentModal } from "@internxt/ui";
import { WithTranslation } from "react-i18next";
import ConnectionStatus from "./ConnectionStatus";
import Preview from "./Preview";
import RecordingWarning from "./RecordingWarning";
import UnsafeRoomWarning from "./UnsafeRoomWarning";
import Video from "../../../media/components/web/Video";
import ConnectionStatus from "../../../premeeting/components/web/ConnectionStatus";
import RecordingWarning from "../../../premeeting/components/web/RecordingWarning";
import UnsafeRoomWarning from "../../../premeeting/components/web/UnsafeRoomWarning";
import Header from "./components/Header";
import { useFullName } from "./hooks/useFullName";
import { useUserData } from "./hooks/useUserData";

interface IProps extends WithTranslation {
/**
Expand Down Expand Up @@ -94,12 +97,19 @@ interface IProps extends WithTranslation {
* The video track to render as preview (if omitted, the default local track will be rendered).
*/
videoTrack?: Object;

/**
* The audio track.
*/

audioTrack?: any;
}

interface UserData {
name: string;
lastname: string;
interface RightContentProps {
isLogged: boolean;
avatar: string | null;
fullName?: string;
t: Function;
}

const useStyles = makeStyles()((theme) => ({
Expand Down Expand Up @@ -165,76 +175,34 @@ const useStyles = makeStyles()((theme) => ({
},
}));

const useUserData = (): UserData | null => {
const xUser = localStorage.getItem("xUser");

return useMemo(() => {
if (!xUser) return null;
try {
return JSON.parse(xUser);
} catch (e) {
console.error("Error parsing user data:", e);
return null;
}
}, [xUser]);
};

const LeftContent = React.memo(
(): JSX.Element => (
<div className="rounded-2xl border bg-black/50 border-white/10 ">
<div
className="flex items-center space-x-2 h-12 px-3"
style={{ paddingLeft: "12px", paddingRight: "12px" }}
>
<img src={"images/internxt_logo.png"} alt="logo" className="h-7" />
<span className="text-lg font-semibold text-white" style={{ fontWeight: 600 }}>
Meet
</span>
<img src={"images/beta.png"} alt="logo" className="h-6" style={{ margin: "0px" }} />
</div>
</div>
)
const AudioMutedIndicator = () => (
<div className="absolute bottom-2 left-2 flex items-center justify-center w-9 h-7 bg-black/50 rounded-[20px]">
<img src="/images/premeetingscreen/MicrophoneSlash.png" alt="Audio Muted" width={16} />
</div>
);

interface RightContentProps {
isLogged: boolean;
avatar: string | null;
fullName?: string;
t: Function;
}

const RightContent = React.memo(({ isLogged, avatar, fullName, t }: RightContentProps): JSX.Element => {
const handleNewMeeting = () => {
alert("Creating new meeting...");
};

const handleLogin = () => {
alert("Redirecting to login...");
};

const handleSignUp = () => {
alert("Redirecting to sign up...");
};
const VideoPreview: React.FC<{ videoTrack: any; isAudioMuted?: boolean }> = ({ videoTrack, isAudioMuted }) => (
<div className="relative w-[264px] h-[147px] rounded-[20px]">
{/* // to remove when finish this view
// <Preview
// videoMuted={videoMuted}
// videoTrack={videoTrack}
// className="w-[264px] h-[147px] rounded-[20px]"
// /> */}
<Video
className="w-[264px] h-[147px] rounded-[20px]"
id="prejoinVideo"
videoTrack={{ jitsiTrack: videoTrack }}
/>
{isAudioMuted && <AudioMutedIndicator />}
</div>
);

return isLogged ? (
<div className="flex space-x-2 flex-row">
<Button variant="primary" onClick={handleNewMeeting}>
{t("meet.preMeeting.newMeeting")}
</Button>
<Avatar src={avatar} fullName={fullName ?? ""} className="text-white" diameter={40} />
</div>
) : (
<div className="flex space-x-2 flex-row">
{/* TODO: Change to secondary variant when dark mode works properly */}
<Button variant="tertiary" onClick={handleLogin}>
{t("meet.login.login")}
</Button>
<Button variant="primary" onClick={handleSignUp}>
{t("meet.login.signUp")}
</Button>
</div>
);
});
const NoVideoPreview = () => (
<div className="w-[264px] h-[147px] rounded-[20px] bg-white/10 flex items-center justify-center">
<img src="/images/VideoCameraSlash.png" alt="No Video" width={60} />
</div>
);

const PreMeetingScreen = ({
_buttons,
Expand All @@ -247,10 +215,16 @@ const PreMeetingScreen = ({
title,
videoMuted,
videoTrack,
audioTrack,
t,
}: IProps) => {
const { classes } = useStyles();
const [isNameInputFocused, setIsNameInputFocused] = useState(false);
const userData = useUserData();
const [userName, setUserName] = useFullName(userData);

const isAudioMuted = audioTrack?.isMuted();
const showNameError = userName.length === 0 && !isNameInputFocused;

const toolbarSection = useMemo(
() => (
Expand All @@ -274,32 +248,59 @@ const PreMeetingScreen = ({
);

return (
<div className={`flex flex-col px-5 ${classes.container}`}>
<Header
leftContent={<LeftContent />}
rightContent={
<RightContent
isLogged={!!userData}
avatar={userData?.avatar ?? null}
fullName={userData ? `${userData.name} ${userData.lastname}` : ""}
t={t}
/>
}
className="z-50 py-3"
/>
<div className="flex flex-row">
<div>
<div className={classes.content}>
<ConnectionStatus />
<div className={classes.contentControls}>
{title && <h1 className={classes.title}>{title}</h1>}
{children}
{toolbarSection}
{warningsSection}
<div className="flex flex-col h-full">
<div className={`flex flex-col px-5 ${classes.container}`}>
<Header userData={userData} translate={t} />
{/* Extract when finish the modal */}
<TransparentModal
className={"flex p-7 bg-black/50 border border-white/15 rounded-[20px]"}
isOpen={true}
onClose={() => {}}
disableBackdrop
>
<div className="flex flex-col h-full text-white space-y-4">
{videoTrack ? (
<VideoPreview videoTrack={videoTrack} isAudioMuted={isAudioMuted} />
) : (
<NoVideoPreview />
)}
<div className="flex mt-7 space-y-2 flex-col">
<Input
variant="default"
accent={showNameError ? "error" : undefined}
onChange={setUserName}
placeholder={t("meet.preMeeting.enterYourName")}
value={userName}
inputClassName="text-white bg-white/10 text-base font-medium text-center rounded-lg"
borderRadius="rounded-lg"
fontClasses="text-base font-medium"
onFocus={() => setIsNameInputFocused(true)}
onBlur={() => setIsNameInputFocused(false)}
/>
{showNameError && (
<div className={`flex flex-grow justify-center items-center text-red`}>
<p className="text-sm">{t("meet.preMeeting.nameRequired")}</p>
</div>
)}
</div>
<Button onClick={() => undefined} disabled={!userName} variant="primary">
{t("meet.preMeeting.joinMeeting")}
</Button>
</div>
</TransparentModal>
<div className="flex flex-row">
<div>
<div className={classes.content}>
<ConnectionStatus />
<div className={classes.contentControls}>
{title && <h1 className={classes.title}>{title}</h1>}
{children}
{toolbarSection}
{warningsSection}
</div>
</div>
</div>
</div>
<Preview videoMuted={videoMuted} videoTrack={videoTrack} />
</div>
</div>
);
Expand Down
Loading
Loading