Skip to content

Commit

Permalink
Merge pull request #310 from vector-im/ptt
Browse files Browse the repository at this point in the history
Add feature-flagged support for Radio/PTT Mode
  • Loading branch information
dbkr authored May 4, 2022
2 parents 2d18953 + 8948152 commit dbef062
Show file tree
Hide file tree
Showing 29 changed files with 1,047 additions and 165 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"editor.tabSize": 2
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"build-storybook": "build-storybook",
"prettier:check": "prettier -c src",
"prettier:format": "prettier -w src",
"lint": "eslint --max-warnings 8 src"
"lint": "eslint --max-warnings 11 src"
},
"dependencies": {
"@juggle/resize-observer": "^3.3.1",
Expand Down
48 changes: 38 additions & 10 deletions src/Facepile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,63 @@ import classNames from "classnames";
import { Avatar } from "./Avatar";
import { getAvatarUrl } from "./matrix-utils";

export function Facepile({ className, client, participants, ...rest }) {
const overlapMap = {
xs: 2,
sm: 4,
md: 8,
};

const sizeMap = {
xs: 24,
sm: 32,
md: 36,
};

export function Facepile({
className,
client,
participants,
max,
size,
...rest
}) {
const _size = sizeMap[size];
const _overlap = overlapMap[size];

return (
<div
className={classNames(styles.facepile, className)}
className={classNames(styles.facepile, styles[size], className)}
title={participants.map((member) => member.name).join(", ")}
style={{ width: participants.length * (_size - _overlap) + _overlap }}
{...rest}
>
{participants.slice(0, 3).map((member, i) => {
{participants.slice(0, max).map((member, i) => {
const avatarUrl = member.user?.avatarUrl;
return (
<Avatar
key={member.userId}
size="xs"
src={avatarUrl && getAvatarUrl(client, avatarUrl, 22)}
size={size}
src={avatarUrl && getAvatarUrl(client, avatarUrl, _size)}
fallback={member.name.slice(0, 1).toUpperCase()}
className={styles.avatar}
style={{ left: i * 22 }}
style={{ left: i * (_size - _overlap) }}
/>
);
})}
{participants.length > 3 && (
{participants.length > max && (
<Avatar
key="additional"
size="xs"
fallback={`+${participants.length - 3}`}
size={size}
fallback={`+${participants.length - max}`}
className={styles.avatar}
style={{ left: 3 * 22 }}
style={{ left: max * (_size - _overlap) }}
/>
)}
</div>
);
}

Facepile.defaultProps = {
max: 3,
size: "xs",
};
17 changes: 16 additions & 1 deletion src/Facepile.module.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
.facepile {
width: 100%;
height: 24px;
position: relative;
}

.facepile.xs {
height: 24px;
}

.facepile.sm {
height: 32px;
}

.facepile.md {
height: 36px;
}

.facepile .avatar {
position: absolute;
top: 0;
border: 1px solid var(--bgColor2);
}

.facepile.md .avatar {
border-width: 2px;
}
25 changes: 25 additions & 0 deletions src/button/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { ReactComponent as VideoIcon } from "../icons/Video.svg";
import { ReactComponent as DisableVideoIcon } from "../icons/DisableVideo.svg";
import { ReactComponent as HangupIcon } from "../icons/Hangup.svg";
import { ReactComponent as ScreenshareIcon } from "../icons/Screenshare.svg";
import { ReactComponent as SettingsIcon } from "../icons/Settings.svg";
import { ReactComponent as AddUserIcon } from "../icons/AddUser.svg";
import { useButton } from "@react-aria/button";
import { mergeProps, useObjectRef } from "@react-aria/utils";
import { TooltipTrigger } from "../Tooltip";
Expand All @@ -20,6 +22,7 @@ export const variantToClassName = {
copy: [styles.copyButton],
iconCopy: [styles.iconCopyButton],
secondaryCopy: [styles.copyButton],
secondaryHangup: [styles.secondaryHangup],
};

export const sizeToClassName = {
Expand Down Expand Up @@ -126,3 +129,25 @@ export function HangupButton({ className, ...rest }) {
</TooltipTrigger>
);
}

export function SettingsButton({ className, ...rest }) {
return (
<TooltipTrigger>
<Button variant="toolbar" {...rest}>
<SettingsIcon />
</Button>
{() => "Settings"}
</TooltipTrigger>
);
}

export function InviteButton({ className, ...rest }) {
return (
<TooltipTrigger>
<Button variant="toolbar" {...rest}>
<AddUserIcon />
</Button>
{() => "Invite"}
</TooltipTrigger>
);
}
9 changes: 9 additions & 0 deletions src/button/Button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ limitations under the License.
.iconButton,
.iconCopyButton,
.secondary,
.secondaryHangup,
.copyButton {
position: relative;
display: flex;
Expand All @@ -34,6 +35,7 @@ limitations under the License.
}

.secondary,
.secondaryHangup,
.button,
.copyButton {
padding: 7px 15px;
Expand All @@ -53,6 +55,7 @@ limitations under the License.
.iconButton:focus,
.iconCopyButton:focus,
.secondary:focus,
.secondaryHangup:focus,
.copyButton:focus {
outline: auto;
}
Expand Down Expand Up @@ -119,6 +122,12 @@ limitations under the License.
background-color: transparent;
}

.secondaryHangup {
color: #ff5b55;
border: 2px solid #ff5b55;
background-color: transparent;
}

.copyButton.secondaryCopy {
color: var(--textColor1);
border-color: var(--textColor1);
Expand Down
16 changes: 15 additions & 1 deletion src/home/RegisteredView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,25 @@ import { JoinExistingCallModal } from "./JoinExistingCallModal";
import { useHistory } from "react-router-dom";
import { Headline, Title } from "../typography/Typography";
import { Form } from "../form/Form";
import { useShouldShowPtt } from "../useShouldShowPtt";

export function RegisteredView({ client }) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const history = useHistory();
const shouldShowPtt = useShouldShowPtt();
const onSubmit = useCallback(
(e) => {
e.preventDefault();
const data = new FormData(e.target);
const roomName = data.get("callName");
const ptt = data.get("ptt") !== null;

async function submit() {
setError(undefined);
setLoading(true);

const roomIdOrAlias = await createRoom(client, roomName);
const roomIdOrAlias = await createRoom(client, roomName, ptt);

if (roomIdOrAlias) {
history.push(`/room/${roomIdOrAlias}`);
Expand Down Expand Up @@ -87,6 +90,7 @@ export function RegisteredView({ client }) {
required
autoComplete="off"
/>

<Button
type="submit"
size="lg"
Expand All @@ -96,6 +100,16 @@ export function RegisteredView({ client }) {
{loading ? "Loading..." : "Go"}
</Button>
</FieldRow>
{shouldShowPtt && (
<FieldRow className={styles.fieldRow}>
<InputField
id="ptt"
name="ptt"
label="Push to Talk"
type="checkbox"
/>
</FieldRow>
)}
{error && (
<FieldRow className={styles.fieldRow}>
<ErrorMessage>{error.message}</ErrorMessage>
Expand Down
4 changes: 4 additions & 0 deletions src/home/RegisteredView.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
}

.fieldRow {
margin-bottom: 24px;
}

.fieldRow:last-child {
margin-bottom: 0;
}

Expand Down
15 changes: 14 additions & 1 deletion src/home/UnauthenticatedView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import { Form } from "../form/Form";
import styles from "./UnauthenticatedView.module.css";
import commonStyles from "./common.module.css";
import { generateRandomName } from "../auth/generateRandomName";
import { useShouldShowPtt } from "../useShouldShowPtt";

export function UnauthenticatedView() {
const shouldShowPtt = useShouldShowPtt();
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const [{ privacyPolicyUrl, recaptchaKey }, register] =
Expand All @@ -28,6 +30,7 @@ export function UnauthenticatedView() {
const data = new FormData(e.target);
const roomName = data.get("callName");
const displayName = data.get("displayName");
const ptt = data.get("ptt") !== null;

async function submit() {
setError(undefined);
Expand All @@ -41,7 +44,7 @@ export function UnauthenticatedView() {
recaptchaResponse,
true
);
const roomIdOrAlias = await createRoom(client, roomName);
const roomIdOrAlias = await createRoom(client, roomName, ptt);

if (roomIdOrAlias) {
history.push(`/room/${roomIdOrAlias}`);
Expand Down Expand Up @@ -111,6 +114,16 @@ export function UnauthenticatedView() {
autoComplete="off"
/>
</FieldRow>
{shouldShowPtt && (
<FieldRow>
<InputField
id="ptt"
name="ptt"
label="Push to Talk"
type="checkbox"
/>
</FieldRow>
)}
<Caption>
By clicking "Go", you agree to our{" "}
<Link href={privacyPolicyUrl}>Terms and conditions</Link>
Expand Down
41 changes: 41 additions & 0 deletions src/input/Toggle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useCallback, useRef } from "react";
import styles from "./Toggle.module.css";
import { useToggleButton } from "@react-aria/button";
import classNames from "classnames";
import { Field } from "./Input";

export function Toggle({ id, label, className, onChange, isSelected }) {
const buttonRef = useRef();
const toggle = useCallback(() => {
onChange(!isSelected);
});
const { buttonProps } = useToggleButton(
{ isSelected },
{ toggle },
buttonRef
);

return (
<Field
className={classNames(
styles.toggle,
{ [styles.on]: isSelected },
className
)}
>
<button
{...buttonProps}
ref={buttonRef}
id={id}
className={classNames(styles.button, {
[styles.isPressed]: isSelected,
})}
>
<div className={styles.ball} />
</button>
<label className={styles.label} htmlFor={id}>
{label}
</label>
</Field>
);
}
46 changes: 46 additions & 0 deletions src/input/Toggle.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.toggle {
align-items: center;
margin-bottom: 20px;
}

.button {
position: relative;
padding: 0;
transition: background-color 0.2s ease-out 0.1s;
width: 44px;
height: 24px;
border: none;
border-radius: 21px;
background-color: #6f7882;
cursor: pointer;
margin-right: 8px;
}

.ball {
transition: left 0.15s ease-out 0.1s;
position: absolute;
width: 20px;
height: 20px;
border-radius: 21px;
background-color: #15191e;
left: 2px;
top: 2px;
}

.label {
padding: 10px 8px;
line-height: 24px;
color: #6f7882;
}

.toggle.on .button {
background-color: #0dbd8b;
}

.toggle.on .ball {
left: 22px;
}

.toggle.on .label {
color: #ffffff;
}
Loading

0 comments on commit dbef062

Please sign in to comment.