(frontend) introduce a menu with more actions

Replaced the existing settings button with a new `OptionsButton` component.
This new component integrates a menu for more actions, providing an
extensible and accessible interface.

Refactoring will follow to enhance code maintainability.

A legacy style was introduced to keep visual coherence with the other controls
from the controls bar. It's temporary, until we redesign the whole bar.
It doesn't match pixel-perfect the legacy styles.

A new public Tchap chan was created. All users, and not only beta users,
can now join it using the link in the menu, and reach us.
This commit is contained in:
lebaudantoine
2024-08-06 17:06:58 +02:00
committed by aleb_the_flash
parent 8c7aed4b00
commit 1715ec10dd
9 changed files with 147 additions and 76 deletions

View File

@@ -0,0 +1,22 @@
import { useTranslation } from 'react-i18next'
import { RiMore2Line } from '@remixicon/react'
import { Button } from '@/primitives'
import { OptionsMenu } from '@/features/rooms/livekit/components/controls/Options/OptionsMenu.tsx'
import { MenuTrigger } from 'react-aria-components'
export const OptionsButton = () => {
const { t } = useTranslation('rooms')
return (
<MenuTrigger>
<Button
square
legacyStyle
aria-label={t('options.buttonLabel')}
tooltip={t('options.buttonLabel')}
>
<RiMore2Line />
</Button>
<OptionsMenu />
</MenuTrigger>
)
}

View File

@@ -0,0 +1,88 @@
import { useTranslation } from 'react-i18next'
import {
RiFeedbackLine,
RiQuestionLine,
RiSettings3Line,
} from '@remixicon/react'
import { styled } from '@/styled-system/jsx'
import {
Menu as RACMenu,
MenuItem as RACMenuItem,
Popover as RACPopover,
} from 'react-aria-components'
// Styled components to be refactored
const StyledMenu = styled(RACMenu, {
base: {
maxHeight: 'inherit',
boxSizing: 'border-box',
overflow: 'auto',
padding: '2px',
minWidth: '150px',
outline: 'none',
},
})
const StyledMenuItem = styled(RACMenuItem, {
base: {
margin: '2px',
padding: '0.286rem 0.571rem',
borderRadius: '6px',
outline: 'none',
cursor: 'default',
color: 'black',
fontSize: '1.072rem',
position: 'relative',
display: 'flex',
alignItems: 'center',
gap: '20px',
forcedColorAdjust: 'none',
'&[data-focused]': {
color: 'primary.text',
backgroundColor: 'primary',
outline: 'none!',
},
},
})
const StyledPopover = styled(RACPopover, {
base: {
border: '1px solid #9ca3af',
boxShadow: '0 8px 20px rgba(0, 0, 0, 0.1)',
borderRadius: '4px',
background: 'white',
color: 'var(--text-color)',
outline: 'none',
minWidth: '112px',
width: '300px',
},
})
export const OptionsMenu = () => {
const { t } = useTranslation('rooms')
return (
<StyledPopover>
<StyledMenu>
<StyledMenuItem
href="https://tchap.gouv.fr/#/room/!aGImQayAgBLjSBycpm:agent.dinum.tchap.gouv.fr?via=agent.dinum.tchap.gouv.fr"
target="_blank"
>
<RiQuestionLine size={18} />
{t('options.items.support')}
</StyledMenuItem>
<StyledMenuItem
href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4"
target="_blank"
>
<RiFeedbackLine size={18} />
{t('options.items.feedbacks')}
</StyledMenuItem>
<StyledMenuItem onAction={() => alert('delete')}>
<RiSettings3Line size={18} />
{t('options.items.settings')}
</StyledMenuItem>
</StyledMenu>
</StyledPopover>
)
}

View File

@@ -1,58 +0,0 @@
import * as React from 'react'
import { useLayoutContext } from '@livekit/components-react'
import { mergeProps } from '@/utils/mergeProps'
/** @alpha */
export interface UseSettingsToggleProps {
props: React.ButtonHTMLAttributes<HTMLButtonElement>
}
/**
* The `useSettingsToggle` hook provides state and functions for toggling the settings menu.
* @remarks
* Depends on the `LayoutContext` to work properly.
* @see {@link SettingsMenu}
* @alpha
*/
export function useSettingsToggle({ props }: UseSettingsToggleProps) {
const { dispatch, state } = useLayoutContext().widget
const className = 'lk-button lk-settings-toggle'
const mergedProps = React.useMemo(() => {
return mergeProps(props, {
className,
onClick: () => {
if (dispatch) dispatch({ msg: 'toggle_settings' })
},
'aria-pressed': state?.showSettings ? 'true' : 'false',
})
}, [props, className, dispatch, state])
return { mergedProps }
}
/** @alpha */
export interface SettingsMenuToggleProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
/**
* The `SettingsMenuToggle` component is a button that toggles the visibility of the `SettingsMenu` component.
* @remarks
* For the component to have any effect it has to live inside a `LayoutContext` context.
*
* @alpha
*/
export const SettingsMenuToggle: (
props: SettingsMenuToggleProps & React.RefAttributes<HTMLButtonElement>
) => React.ReactNode = /* @__PURE__ */ React.forwardRef<
HTMLButtonElement,
SettingsMenuToggleProps
>(function SettingsMenuToggle(props: SettingsMenuToggleProps, ref) {
const { mergedProps } = useSettingsToggle({ props })
return (
<button ref={ref} {...mergedProps}>
{props.children}
</button>
)
})

View File

@@ -7,7 +7,6 @@ import {
ChatIcon,
ChatToggle,
DisconnectButton,
GearIcon,
LeaveIcon,
MediaDeviceMenu,
TrackToggle,
@@ -15,12 +14,11 @@ import {
usePersistentUserChoices,
} from '@livekit/components-react'
import { SettingsMenuToggle } from '../components/controls/SettingsMenuToggle'
import { mergeProps } from '@/utils/mergeProps.ts'
import { StartMediaButton } from '../components/controls/StartMediaButton'
import { useMediaQuery } from '../hooks/useMediaQuery'
import { useTranslation } from 'react-i18next'
import { SettingsButton } from '@/features/settings'
import { OptionsButton } from '../components/controls/Options/OptionsButton'
/** @public */
export type ControlBarControls = {
@@ -189,10 +187,7 @@ export function ControlBar({
{showIcon && <ChatIcon />}
{showText && t('controls.chat')}
</ChatToggle>
<SettingsMenuToggle>
{showIcon && <GearIcon />}
{showText && t('controls.settings')}
</SettingsMenuToggle>
<OptionsButton />
<DisconnectButton>
{showIcon && <LeaveIcon />}
{showText && t('controls.leave')}

View File

@@ -187,14 +187,6 @@ export function VideoConference({
messageEncoder={chatMessageEncoder}
messageDecoder={chatMessageDecoder}
/>
{SettingsComponent && (
<div
className="lk-settings-menu-modal"
style={{ display: widgetState.showSettings ? 'block' : 'none' }}
>
<SettingsComponent />
</div>
)}
</LayoutContextProvider>
)}
<RoomAudioRenderer />

View File

@@ -29,7 +29,14 @@
"shareScreen": "",
"stopScreenShare": "",
"chat": "",
"settings": "",
"leave": ""
},
"options": {
"buttonLabel": "",
"items": {
"feedbacks": "",
"support": "",
"settings": ""
}
}
}

View File

@@ -29,7 +29,14 @@
"shareScreen": "Share screen",
"stopScreenShare": "Stop screen share",
"chat": "Chat",
"settings": "Settings",
"leave": "Leave"
},
"options": {
"buttonLabel": "More Options",
"items": {
"feedbacks": "Give us feedbacks",
"support": "Get Help on Tchap",
"settings": "Settings"
}
}
}

View File

@@ -29,7 +29,14 @@
"shareScreen": "Partager l'écran",
"stopScreenShare": "Arrêter le partage",
"chat": "Chat",
"settings": "Paramètres",
"leave": "Quitter"
},
"options": {
"buttonLabel": "Plus d'options",
"items": {
"feedbacks": "Partager votre avis",
"support": "Obtenir de l'aide sur Tchap",
"settings": "Paramètres"
}
}
}

View File

@@ -103,6 +103,17 @@ const button = cva({
width: 'full',
},
},
legacyStyle: {
true: {
borderColor: 'gray.400',
'&[data-hovered]': {
borderColor: 'gray.500',
},
'&[data-pressed]': {
borderColor: 'gray.500',
},
},
},
},
defaultVariants: {
size: 'default',