This commit is contained in:
David Baker
2022-06-14 17:17:28 +01:00
parent 24ca771405
commit 4222228556
6 changed files with 65 additions and 28 deletions

1
.env
View File

@@ -22,6 +22,7 @@
# VITE_THEME_PRIMARY_CONTENT=#ffffff # VITE_THEME_PRIMARY_CONTENT=#ffffff
# VITE_THEME_SECONDARY_CONTENT=#a9b2bc # VITE_THEME_SECONDARY_CONTENT=#a9b2bc
# VITE_THEME_TERTIARY_CONTENT=#8e99a4 # VITE_THEME_TERTIARY_CONTENT=#8e99a4
# VITE_THEME_TERTIARY_CONTENT_20=#8e99a433
# VITE_THEME_QUATERNARY_CONTENT=#6f7882 # VITE_THEME_QUATERNARY_CONTENT=#6f7882
# VITE_THEME_QUINARY_CONTENT=#394049 # VITE_THEME_QUINARY_CONTENT=#394049
# VITE_THEME_SYSTEM=#21262c # VITE_THEME_SYSTEM=#21262c

View File

@@ -33,6 +33,7 @@ limitations under the License.
--primary-content: #ffffff; --primary-content: #ffffff;
--secondary-content: #a9b2bc; --secondary-content: #a9b2bc;
--tertiary-content: #8e99a4; --tertiary-content: #8e99a4;
--tertiary-content-20: #8e99a433;
--quaternary-content: #6f7882; --quaternary-content: #6f7882;
--quinary-content: #394049; --quinary-content: #394049;
--system: #21262c; --system: #21262c;

View File

@@ -61,6 +61,10 @@ if (import.meta.env.VITE_CUSTOM_THEME) {
"--tertiary-content", "--tertiary-content",
import.meta.env.VITE_THEME_TERTIARY_CONTENT as string import.meta.env.VITE_THEME_TERTIARY_CONTENT as string
); );
style.setProperty(
"--tertiary-content-20",
import.meta.env.VITE_THEME_TERTIARY_CONTENT_20 as string
);
style.setProperty( style.setProperty(
"--quaternary-content", "--quaternary-content",
import.meta.env.VITE_THEME_QUATERNARY_CONTENT as string import.meta.env.VITE_THEME_QUATERNARY_CONTENT as string

View File

@@ -17,6 +17,12 @@
cursor: unset; cursor: unset;
} }
.networkWaiting {
background-color: var(--tertiary-content);
border-color: var(--tertiary-content);
cursor: unset;
}
.error { .error {
background-color: var(--alert); background-color: var(--alert);
border-color: var(--alert); border-color: var(--alert);

View File

@@ -32,12 +32,9 @@ interface Props {
size: number; size: number;
startTalking: () => void; startTalking: () => void;
stopTalking: () => void; stopTalking: () => void;
} networkWaiting: boolean;
enqueueNetworkWaiting: (value: boolean, delay: number) => void;
interface State { setNetworkWaiting: (value: boolean) => void;
isHeld: boolean;
// If the button is being pressed by touch, the ID of that touch
activeTouchID: number | null;
} }
export const PTTButton: React.FC<Props> = ({ export const PTTButton: React.FC<Props> = ({
@@ -50,19 +47,31 @@ export const PTTButton: React.FC<Props> = ({
size, size,
startTalking, startTalking,
stopTalking, stopTalking,
networkWaiting,
enqueueNetworkWaiting,
setNetworkWaiting,
}) => { }) => {
const buttonRef = createRef<HTMLButtonElement>(); const buttonRef = createRef<HTMLButtonElement>();
const [{ isHeld, activeTouchID }, setState] = useState<State>({ const [held, setHeld] = useState(false);
isHeld: false, const [activeTouchId, setActiveTouchId] = useState<number | null>(null);
activeTouchID: null,
}); const hold = useCallback(() => {
setHeld(true);
// This update is delayed so the user only sees it if latency is significant
enqueueNetworkWaiting(true, 100);
}, [setHeld, enqueueNetworkWaiting]);
const unhold = useCallback(() => {
setHeld(false);
setNetworkWaiting(false);
}, [setHeld, setNetworkWaiting]);
const onWindowMouseUp = useCallback( const onWindowMouseUp = useCallback(
(e) => { (e) => {
if (isHeld) stopTalking(); if (held) stopTalking();
setState({ isHeld: false, activeTouchID: null }); unhold();
}, },
[isHeld, setState, stopTalking] [held, unhold, stopTalking]
); );
const onWindowTouchEnd = useCallback( const onWindowTouchEnd = useCallback(
@@ -72,7 +81,7 @@ export const PTTButton: React.FC<Props> = ({
// have to do this a really old-school way). // have to do this a really old-school way).
let touchFound = false; let touchFound = false;
for (let i = 0; i < e.changedTouches.length; ++i) { for (let i = 0; i < e.changedTouches.length; ++i) {
if (e.changedTouches.item(i).identifier === activeTouchID) { if (e.changedTouches.item(i).identifier === activeTouchId) {
touchFound = true; touchFound = true;
break; break;
} }
@@ -80,34 +89,33 @@ export const PTTButton: React.FC<Props> = ({
if (!touchFound) return; if (!touchFound) return;
e.preventDefault(); e.preventDefault();
if (isHeld) stopTalking(); if (held) stopTalking();
setState({ isHeld: false, activeTouchID: null }); unhold();
setActiveTouchId(null);
}, },
[isHeld, activeTouchID, setState, stopTalking] [held, activeTouchId, unhold, stopTalking]
); );
const onButtonMouseDown = useCallback( const onButtonMouseDown = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => { (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault(); e.preventDefault();
setState({ isHeld: true, activeTouchID: null }); hold();
startTalking(); startTalking();
}, },
[setState, startTalking] [hold, startTalking]
); );
const onButtonTouchStart = useCallback( const onButtonTouchStart = useCallback(
(e: TouchEvent) => { (e: TouchEvent) => {
e.preventDefault(); e.preventDefault();
if (isHeld) return; if (!held) {
hold();
setState({ setActiveTouchId(e.changedTouches.item(0).identifier);
isHeld: true, startTalking();
activeTouchID: e.changedTouches.item(0).identifier, }
});
startTalking();
}, },
[isHeld, setState, startTalking] [held, hold, startTalking]
); );
useEffect(() => { useEffect(() => {
@@ -143,12 +151,15 @@ export const PTTButton: React.FC<Props> = ({
}); });
const shadowColor = showTalkOverError const shadowColor = showTalkOverError
? "var(--alert-20)" ? "var(--alert-20)"
: networkWaiting
? "var(--tertiary-content-20)"
: "var(--accent-20)"; : "var(--accent-20)";
return ( return (
<animated.button <animated.button
className={classNames(styles.pttButton, { className={classNames(styles.pttButton, {
[styles.talking]: activeSpeakerUserId, [styles.talking]: activeSpeakerUserId,
[styles.networkWaiting]: networkWaiting,
[styles.error]: showTalkOverError, [styles.error]: showTalkOverError,
})} })}
style={{ style={{

View File

@@ -20,6 +20,7 @@ import { ResizeObserver } from "@juggle/resize-observer";
import { GroupCall, MatrixClient, RoomMember } from "matrix-js-sdk"; import { GroupCall, MatrixClient, RoomMember } from "matrix-js-sdk";
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed"; import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
import { useDelayedState } from "../useDelayedState";
import { useModalTriggerState } from "../Modal"; import { useModalTriggerState } from "../Modal";
import { InviteModal } from "./InviteModal"; import { InviteModal } from "./InviteModal";
import { HangupButton, InviteButton } from "../button"; import { HangupButton, InviteButton } from "../button";
@@ -39,6 +40,7 @@ import { GroupCallInspector } from "./GroupCallInspector";
import { OverflowMenu } from "./OverflowMenu"; import { OverflowMenu } from "./OverflowMenu";
function getPromptText( function getPromptText(
networkWaiting: boolean,
showTalkOverError: boolean, showTalkOverError: boolean,
pttButtonHeld: boolean, pttButtonHeld: boolean,
activeSpeakerIsLocalUser: boolean, activeSpeakerIsLocalUser: boolean,
@@ -47,10 +49,14 @@ function getPromptText(
activeSpeakerDisplayName: string, activeSpeakerDisplayName: string,
connected: boolean connected: boolean
): string { ): string {
if (!connected) return "Connection Lost"; if (!connected) return "Connection lost";
const isTouchScreen = Boolean(window.ontouchstart !== undefined); const isTouchScreen = Boolean(window.ontouchstart !== undefined);
if (networkWaiting) {
return "Waiting for network";
}
if (showTalkOverError) { if (showTalkOverError) {
return "You can't talk at the same time"; return "You can't talk at the same time";
} }
@@ -136,7 +142,11 @@ export const PTTCallView: React.FC<Props> = ({
!feedbackModalState.isOpen !feedbackModalState.isOpen
); );
const [talkingExpected, enqueueTalkingExpected, setTalkingExpected] =
useDelayedState(false);
const showTalkOverError = pttButtonHeld && transmitBlocked; const showTalkOverError = pttButtonHeld && transmitBlocked;
const networkWaiting =
talkingExpected && !activeSpeakerUserId && !showTalkOverError;
const activeSpeakerIsLocalUser = const activeSpeakerIsLocalUser =
activeSpeakerUserId && client.getUserId() === activeSpeakerUserId; activeSpeakerUserId && client.getUserId() === activeSpeakerUserId;
@@ -226,9 +236,13 @@ export const PTTCallView: React.FC<Props> = ({
size={pttButtonSize} size={pttButtonSize}
startTalking={startTalking} startTalking={startTalking}
stopTalking={stopTalking} stopTalking={stopTalking}
networkWaiting={networkWaiting}
enqueueNetworkWaiting={enqueueTalkingExpected}
setNetworkWaiting={setTalkingExpected}
/> />
<p className={styles.actionTip}> <p className={styles.actionTip}>
{getPromptText( {getPromptText(
networkWaiting,
showTalkOverError, showTalkOverError,
pttButtonHeld, pttButtonHeld,
activeSpeakerIsLocalUser, activeSpeakerIsLocalUser,