♻️(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:
committed by
aleb_the_flash
parent
398ef1ae8a
commit
57a7523cc4
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ import { useConfig } from '@/api/useConfig'
|
|||||||
import { FeatureFlags } from '@/features/analytics/enums'
|
import { FeatureFlags } from '@/features/analytics/enums'
|
||||||
import { NoAccessView } from './NoAccessView'
|
import { NoAccessView } from './NoAccessView'
|
||||||
import { HStack, VStack } from '@/styled-system/jsx'
|
import { HStack, VStack } from '@/styled-system/jsx'
|
||||||
|
import { RowWrapper } from './RowWrapper'
|
||||||
import { Checkbox } from '@/primitives/Checkbox'
|
import { Checkbox } from '@/primitives/Checkbox'
|
||||||
import { useTranscriptionLanguage } from '@/features/settings'
|
import { useTranscriptionLanguage } from '@/features/settings'
|
||||||
|
|
||||||
@@ -188,63 +189,12 @@ export const ScreenRecordingSidePanel = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
<VStack gap={0} marginBottom={40}>
|
<VStack gap={0} marginBottom={40}>
|
||||||
<div
|
<RowWrapper iconName="cloud_download" position="first">
|
||||||
className={css({
|
<Text variant="sm">{t('details.destination')}</Text>
|
||||||
width: '100%',
|
</RowWrapper>
|
||||||
background: 'gray.100',
|
<RowWrapper iconName="mail" position="last">
|
||||||
borderRadius: '4px 4px 0 0',
|
<Text variant="sm">{t('details.receiver')}</Text>
|
||||||
paddingLeft: '4px',
|
</RowWrapper>
|
||||||
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>
|
|
||||||
|
|
||||||
<div className={css({ height: '15px' })} />
|
<div className={css({ height: '15px' })} />
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import posthog from 'posthog-js'
|
|||||||
import { useSnapshot } from 'valtio/index'
|
import { useSnapshot } from 'valtio/index'
|
||||||
import { Spinner } from '@/primitives/Spinner'
|
import { Spinner } from '@/primitives/Spinner'
|
||||||
import { useConfig } from '@/api/useConfig'
|
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 { Checkbox } from '@/primitives/Checkbox.tsx'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -34,6 +34,7 @@ import {
|
|||||||
useTranscriptionLanguage,
|
useTranscriptionLanguage,
|
||||||
} from '@/features/settings'
|
} from '@/features/settings'
|
||||||
import { NoAccessView } from './NoAccessView'
|
import { NoAccessView } from './NoAccessView'
|
||||||
|
import { RowWrapper } from './RowWrapper'
|
||||||
|
|
||||||
export const TranscriptSidePanel = () => {
|
export const TranscriptSidePanel = () => {
|
||||||
const { data } = useConfig()
|
const { data } = useConfig()
|
||||||
@@ -214,123 +215,41 @@ export const TranscriptSidePanel = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
<VStack gap={0} marginBottom={40}>
|
<VStack gap={0} marginBottom={40}>
|
||||||
<div
|
<RowWrapper iconName="article" position="first">
|
||||||
className={css({
|
<Text variant="sm">
|
||||||
width: '100%',
|
{data?.transcription_destination ? (
|
||||||
// border: '1px solid black',
|
<>
|
||||||
background: 'gray.100',
|
{t('details.destination')}{' '}
|
||||||
borderRadius: '4px 4px 0 0',
|
<A
|
||||||
paddingLeft: '4px',
|
href={data.transcription_destination}
|
||||||
padding: '8px',
|
target="_blank"
|
||||||
display: 'flex',
|
rel="noopener noreferrer"
|
||||||
})}
|
>
|
||||||
>
|
{data.transcription_destination.replace('https://', '')}
|
||||||
<div
|
</A>
|
||||||
className={css({
|
</>
|
||||||
flex: 1,
|
) : (
|
||||||
display: 'flex',
|
t('details.destinationUnknown')
|
||||||
justifyContent: 'center',
|
)}
|
||||||
alignItems: 'center',
|
</Text>
|
||||||
})}
|
</RowWrapper>
|
||||||
>
|
<RowWrapper iconName="mail">
|
||||||
<span className="material-icons">article</span>
|
<Text variant="sm">{t('details.receiver')}</Text>
|
||||||
</div>
|
</RowWrapper>
|
||||||
<div
|
<RowWrapper iconName="language" position="last">
|
||||||
className={css({
|
<Text variant="sm">{t('details.language')}</Text>
|
||||||
flex: 5,
|
<Text variant="sm">
|
||||||
})}
|
<Button
|
||||||
>
|
variant="text"
|
||||||
<Text variant="sm">
|
size="xs"
|
||||||
{data?.transcription_destination ? (
|
onPress={() =>
|
||||||
<>
|
openSettingsDialog(SettingsDialogExtendedKey.TRANSCRIPTION)
|
||||||
{t('details.destination')}{' '}
|
}
|
||||||
<A
|
>
|
||||||
href={data.transcription_destination}
|
{selectedLanguageLabel}
|
||||||
target="_blank"
|
</Button>
|
||||||
rel="noopener noreferrer"
|
</Text>
|
||||||
>
|
</RowWrapper>
|
||||||
{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>
|
|
||||||
|
|
||||||
<div className={css({ height: '15px' })} />
|
<div className={css({ height: '15px' })} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user