Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
102 changes: 93 additions & 9 deletions src/components/navbars/appNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import {
useTheme
} from '@mui/material';

const currTheme = createTheme({
palette: {
primary: {
main: '#ffffff',
contrastText: '#000000'
},
},
});
// const currTheme = createTheme({
// palette: {
// primary: {
// main: '#ffffff',
// contrastText: '#000000'
// },
// },
// });

export default function AppNavBar(props) {
const [isDrawerOpen, setDrawerOpen] = React.useState(false);
Expand All @@ -41,6 +41,81 @@ export default function AppNavBar(props) {
const apiDisplayName = API_Name(apiStatus.currentApi);
const accentColor = displayStatus.secondaryColor;

const TOP_TRIGGER_ZONE = 48; // px from top to reveal
const HIDE_TIMEOUT_MS = 2500;
const [topbarLocked, setTopbarLocked] = React.useState<boolean>(false);
const [topbarVisible, setTopbarVisible] = React.useState<boolean>(true);
const hideRef = React.useRef<number | null>(null);
const pointerStartY = React.useRef<number | null>(null);
const pointerId = React.useRef<number | null>(null);

const showTopbar = React.useCallback(() => {
setTopbarVisible(true);
if (hideRef.current) {window.clearTimeout(hideRef.current); hideRef.current = null;}
if (!topbarLocked) {
hideRef.current = window.setTimeout(() => setTopbarVisible(false), HIDE_TIMEOUT_MS);
}
}, [topbarLocked]);

React.useEffect(() => {showTopbar();}, [showTopbar]); //to activate immediately

React.useEffect(() => {
const onPointerDown = (e: PointerEvent) => {
if(pointerId.current !== null) return;

const y = e.clientY;
pointerId.current = e.pointerId;
pointerStartY.current = y;

if (y <= TOP_TRIGGER_ZONE) {
showTopbar();
}
};

const onPointerMove = (e: PointerEvent) => {
// proximity check
if (e.clientY <= TOP_TRIGGER_ZONE) {
showTopbar();
}
// swipe down check
if (e.pointerId !== pointerId.current || pointerStartY.current === null) {
return;
}
const y = e.clientY
const delta = y - (pointerStartY.current ?? 0);
const SWIPE_THRESHOLD = 40; //how far to swipe

if(pointerStartY.current <= TOP_TRIGGER_ZONE && delta >= SWIPE_THRESHOLD) {
showTopbar();

// reset
pointerStartY.current = null;
pointerId.current = null;
}
};

const onPointerUp = (e: PointerEvent) => {
if(e.pointerId === pointerId.current) {
pointerId.current = null;
pointerStartY.current = null;
}
};

window.addEventListener('pointerdown', onPointerDown, {passive: true });
window.addEventListener('pointermove', onPointerMove, {passive: true });
window.addEventListener('pointerup', onPointerUp);
window.addEventListener('pointercancel', onPointerUp);

return () => {
window.removeEventListener('pointerdown', onPointerDown);
window.removeEventListener('pointermove', onPointerMove);
window.removeEventListener('pointerup', onPointerUp);
window.removeEventListener('pointercancel', onPointerUp);
};
}, [showTopbar]);

React.useEffect(() => () => {if (hideRef.current) window.clearTimeout(hideRef.current); }, []);

const theme = useTheme();
// const isMobile = useMediaQuery(theme.breakpoints.down('sm'));

Expand All @@ -54,7 +129,7 @@ export default function AppNavBar(props) {

return (
<ThemeProvider theme={theme}>
<AppBar position="fixed" id="topbar-wrapper" sx={{ transition: '0.6s', backgroundColor: accentColor }}>
<AppBar position="fixed" id="topbar-wrapper" sx={{ transition: 'transform 240ms ease-in-out', transform: topbarVisible ? 'translateY(0)' : 'translateY(-100%)', backgroundColor: accentColor }}>
<Toolbar sx={{ width: '100%', minHeight: 56, color: 'white' }}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid container alignItems="center" justifyContent="space-between" sx={{ width: '100%' }}>
Expand Down Expand Up @@ -89,6 +164,15 @@ export default function AppNavBar(props) {
apiDisplayName={apiDisplayName}
listening={controlStatus.listening}
menuVisible={displayStatus.menuVisible}

topbarLocked={topbarLocked}
onTopBarToggle= {(v) => {
setTopbarLocked(v);
if(v) {
setTopbarVisible(true);
if (hideRef.current) {window.clearTimeout(hideRef.current); hideRef.current=null; }
}
}}
/>
</Grid>
</Grid>
Expand Down
10 changes: 5 additions & 5 deletions src/components/navbars/topbar/apiDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ export default function ApiDropdown(props) {
{isWhisperActive ? <WhisperIcon /> : null}
<span>{props.apiDisplayName}</span>
<Tooltip title="API choice">
<IconButton onClick={handleClick}>
<ThemeProvider theme={currTheme}>
{open ? <ExpandLess color="primary" fontSize="large" /> : <ExpandMore color="primary" fontSize="large" />}
</ThemeProvider>
</IconButton>
<ThemeProvider theme={currTheme}>
<IconButton color='primary' onClick={handleClick}>
{open ? <ExpandLess fontSize="large" /> : <ExpandMore fontSize="large" />}
</IconButton>
</ThemeProvider>
</Tooltip>
</Box>
<Menu
Expand Down
4 changes: 2 additions & 2 deletions src/components/navbars/topbar/listening.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import * as React from 'react';
{props.listening === false ? "Begin Listening" : "Pause Listening"}>
<IconButton color="primary" onClick={handleClick}>
{props.listening ?
<MicIcon color="primary" fontSize="large"/> :
<MicOffIcon color="primary" fontSize="large"/>
<MicIcon fontSize="large"/> :
<MicOffIcon fontSize="large"/>
}
</IconButton>
</Tooltip>
Expand Down
37 changes: 37 additions & 0 deletions src/components/navbars/topbar/lock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { LockIcon, LockOpenIcon, ThemeProvider, IconButton, Tooltip } from "../../../muiImports"
import * as React from 'react';
import Theme from '../../theme'

interface Props {
locked?: boolean;
onToggle?: (locked: boolean) => void;
}

export default function LockToggle(props: Props) {
const {locked: lockedProp, onToggle} = props;
const [locked, setLocked] = React.useState<boolean>(!!lockedProp);

React.useEffect(()=>{
if(typeof lockedProp === 'boolean') setLocked(lockedProp);
}, [lockedProp]);

const handleClick = () => {
const next = !locked;
setLocked(next);
onToggle?.(next);
};
const {myTheme} = Theme()
// color "primary" comes from Theme()
return (
<div>
<ThemeProvider theme={myTheme}>
<Tooltip title = {locked ? "Unlock (hide)" : "Lock (keep visible)"}>
<IconButton color="primary" onClick={handleClick}>
{locked ? <LockIcon /> : <LockOpenIcon />}
</IconButton>
</Tooltip>
</ThemeProvider>

</div>
);
}
8 changes: 6 additions & 2 deletions src/components/navbars/topbar/topBar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import * as React from 'react';
import { Grid, Box } from '../../../muiImports';
import { /*Grid,*/ Box } from '../../../muiImports';

import ApiDropdown from './apiDropdown';
import Fullscreen from './fullScreen';
import Listening from './listening';
import QRCodeScreen from './qrCodeScreen';
import MenuHider from './menuHider';
import LockToggle from './lock';
// import MenuHider from './menuHider';
import TranscriptDownload from './transcriptDownload';

import {
Expand All @@ -20,6 +21,8 @@ import {

const iconSize = isMobile ? "small" : "medium";

const {topbarLocked, onTopBarToggle } = props;

return (
<Box sx={{
display: 'flex',
Expand All @@ -37,6 +40,7 @@ import {
{/* Only display if there is enough space */}
{<Listening listening={props.listening} iconSize={iconSize} />}
{/* {!isMobile && <MenuHider menuVisible={props.menuVisible} iconSize={iconSize} />} */}
{<LockToggle locked = {topbarLocked} onToggle={onTopBarToggle}/>}
{<TranscriptDownload />}
{ <Fullscreen iconSize={iconSize} />}
{!isMobile && <QRCodeScreen />}
Expand Down