♻️(frontend) extract recording row layout in reusable component

Now that screen recording and transcription share the same UI presentation,
extract the row logic into a reusable component to avoid code duplication and
improve code maintainability.
This commit is contained in:
lebaudantoine
2025-12-31 22:53:33 +01:00
committed by aleb_the_flash
parent 398ef1ae8a
commit 57a7523cc4
3 changed files with 104 additions and 175 deletions

View File

@@ -0,0 +1,60 @@
import { css } from '@/styled-system/css'
import { ReactNode } from 'react'
type RowPosition = 'first' | 'middle' | 'last' | 'single'
const BORDER_RADIUS_MAP: Record<RowPosition, string> = {
first: '4px 4px 0 0',
middle: '0',
last: '0 0 4px 4px',
single: '4px',
} as const
interface RowWrapperProps {
iconName: string
children: ReactNode
position?: RowPosition
}
export const RowWrapper = ({
iconName,
children,
position = 'middle',
}: RowWrapperProps) => {
return (
<div
style={{
borderRadius: BORDER_RADIUS_MAP[position],
}}
className={css({
width: '100%',
background: 'gray.100',
padding: '8px',
display: 'flex',
marginTop: '4px',
})}
>
<div
className={css({
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
})}
>
{/* fixme - doesn't handle properly material-symbols */}
<span className="material-icons">{iconName}</span>
</div>
<div
className={css({
flex: 5,
display: 'flex',
alignItems: 'center',
gap: '0.25rem',
})}
>
{children}
</div>
</div>
)
}

View File

@@ -27,6 +27,7 @@ import { useConfig } from '@/api/useConfig'
import { FeatureFlags } from '@/features/analytics/enums'
import { NoAccessView } from './NoAccessView'
import { HStack, VStack } from '@/styled-system/jsx'
import { RowWrapper } from './RowWrapper'
import { Checkbox } from '@/primitives/Checkbox'
import { useTranscriptionLanguage } from '@/features/settings'
@@ -188,63 +189,12 @@ export const ScreenRecordingSidePanel = () => {
</Text>
</VStack>
<VStack gap={0} marginBottom={40}>
<div
className={css({
width: '100%',
background: 'gray.100',
borderRadius: '4px 4px 0 0',
paddingLeft: '4px',
padding: '8px',
display: 'flex',
})}
>
<div
className={css({
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
})}
>
<span className="material-icons">cloud_download</span>
</div>
<div
className={css({
flex: 5,
})}
>
<Text variant="sm">{t('details.destination')}</Text>
</div>
</div>
<div
className={css({
width: '100%',
background: 'gray.100',
borderRadius: '0 0 4px 4px',
paddingLeft: '4px',
padding: '8px',
display: 'flex',
marginTop: '4px',
})}
>
<div
className={css({
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
})}
>
<span className="material-icons">mail</span>
</div>
<div
className={css({
flex: 5,
})}
>
<Text variant="sm">{t('details.receiver')}</Text>
</div>
</div>
<RowWrapper iconName="cloud_download" position="first">
<Text variant="sm">{t('details.destination')}</Text>
</RowWrapper>
<RowWrapper iconName="mail" position="last">
<Text variant="sm">{t('details.receiver')}</Text>
</RowWrapper>
<div className={css({ height: '15px' })} />

View File

@@ -25,7 +25,7 @@ import posthog from 'posthog-js'
import { useSnapshot } from 'valtio/index'
import { Spinner } from '@/primitives/Spinner'
import { useConfig } from '@/api/useConfig'
import { HStack, VStack } from '@/styled-system/jsx'
import { VStack } from '@/styled-system/jsx'
import { Checkbox } from '@/primitives/Checkbox.tsx'
import {
@@ -34,6 +34,7 @@ import {
useTranscriptionLanguage,
} from '@/features/settings'
import { NoAccessView } from './NoAccessView'
import { RowWrapper } from './RowWrapper'
export const TranscriptSidePanel = () => {
const { data } = useConfig()
@@ -214,123 +215,41 @@ export const TranscriptSidePanel = () => {
</Text>
</VStack>
<VStack gap={0} marginBottom={40}>
<div
className={css({
width: '100%',
// border: '1px solid black',
background: 'gray.100',
borderRadius: '4px 4px 0 0',
paddingLeft: '4px',
padding: '8px',
display: 'flex',
})}
>
<div
className={css({
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
})}
>
<span className="material-icons">article</span>
</div>
<div
className={css({
flex: 5,
})}
>
<Text variant="sm">
{data?.transcription_destination ? (
<>
{t('details.destination')}{' '}
<A
href={data.transcription_destination}
target="_blank"
rel="noopener noreferrer"
>
{data.transcription_destination.replace('https://', '')}
</A>
</>
) : (
t('details.destinationUnknown')
)}
</Text>
</div>
</div>
<div
className={css({
width: '100%',
// border: '1px solid black',
background: 'gray.100',
paddingLeft: '4px',
padding: '8px',
display: 'flex',
marginTop: '4px',
})}
>
<div
className={css({
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
})}
>
<span className="material-icons">mail</span>
</div>
<div
className={css({
flex: 5,
})}
>
<Text variant="sm">{t('details.receiver')}</Text>
</div>
</div>
<div
className={css({
width: '100%',
background: 'gray.100',
borderRadius: '0 0 4px 4px',
paddingLeft: '4px',
padding: '8px',
display: 'flex',
marginTop: '4px',
})}
>
<div
className={css({
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
})}
>
<span className="material-icons">language</span>
</div>
<div
className={css({
flex: 5,
display: 'flex',
alignItems: 'center',
gap: '0.25rem',
})}
>
<Text variant="sm">{t('details.language')}</Text>
<Text variant="sm">
<Button
variant="text"
size="xs"
onPress={() =>
openSettingsDialog(SettingsDialogExtendedKey.TRANSCRIPTION)
}
>
{selectedLanguageLabel}
</Button>
</Text>
</div>
</div>
<RowWrapper iconName="article" position="first">
<Text variant="sm">
{data?.transcription_destination ? (
<>
{t('details.destination')}{' '}
<A
href={data.transcription_destination}
target="_blank"
rel="noopener noreferrer"
>
{data.transcription_destination.replace('https://', '')}
</A>
</>
) : (
t('details.destinationUnknown')
)}
</Text>
</RowWrapper>
<RowWrapper iconName="mail">
<Text variant="sm">{t('details.receiver')}</Text>
</RowWrapper>
<RowWrapper iconName="language" position="last">
<Text variant="sm">{t('details.language')}</Text>
<Text variant="sm">
<Button
variant="text"
size="xs"
onPress={() =>
openSettingsDialog(SettingsDialogExtendedKey.TRANSCRIPTION)
}
>
{selectedLanguageLabel}
</Button>
</Text>
</RowWrapper>
<div className={css({ height: '15px' })} />
<div