Files
pawol.nu/components/teks/teks.js
T
cedric d66c679fd1
Déploiement FRONT BETA / check (push) Successful in 2m11s
Déploiement FRONT PROD / check (push) Successful in 2m11s
Déploiement FRONT BETA / deploy (push) Successful in 21s
Déploiement FRONT PROD / deploy (push) Successful in 20s
revert: ne plus présélectionner selon la langue du navigateur
2026-07-06 02:34:08 +04:00

340 lines
12 KiB
JavaScript

'use client'
import {useEffect, useState} from 'react'
import PropTypes from 'prop-types'
import Box from '@mui/material/Box'
import Select from '@mui/material/Select'
import MenuItem from '@mui/material/MenuItem'
import ListSubheader from '@mui/material/ListSubheader'
import Divider from '@mui/material/Divider'
import FormControl from '@mui/material/FormControl'
import Typography from '@mui/material/Typography'
import Tooltip from '@mui/material/Tooltip'
import {useMediaQuery} from '@mui/material'
import Link from 'next/link'
import slugify from 'slugify'
import {styled} from '@mui/material/styles'
import ExplicitIcon from '@mui/icons-material/Explicit'
import TranslateIcon from '@mui/icons-material/Translate'
import StarIcon from '@mui/icons-material/Star'
import ArticleIcon from '@mui/icons-material/Article'
import Image from 'next/image'
import {formatJsonString} from '../../lib/utils/format'
import {getAlias} from '../../lib/utils/get-alias'
import {formatKuveti} from '../../lib/kuveti'
import LicenseModal from '../cc/license-modal'
import FilesDialog from '../files/files-dialog'
import KitsDialog from '../files/kits-dialog'
import {StreamButton} from '../streaming-buttons'
import EntegreMizik from './entegre-mizik'
import OkiMizik from './oki-mizik'
import DiferansDialog from './diferans-dialog'
import Komante from './komante'
import ReponsMastodon from './repons-mastodon'
import {useCurrentTrack} from './current-track-context'
const IMAGE_URL = process.env.NEXT_PUBLIC_API_URL_ROOT || 'http://localhost:1337'
const PREFIX = 'teks'
const classes = {
container: `${PREFIX}-container`,
tooltip: `${PREFIX}-tooltip`,
text: `${PREFIX}-text`,
gridText: `${PREFIX}-gridText`,
grid: `${PREFIX}-grid`,
koute: `${PREFIX}-koute`,
vwe: `${PREFIX}-vwe`,
pataje: `${PREFIX}-pataje`,
separation: `${PREFIX}-separation`
}
const Root = styled('div')((
{
theme
}
) => ({
[`&.${classes.container}`]: {
marginTop: '3em',
},
[`& .${classes.gridText}`]: {
border: '2px solid grey',
borderRadius: '5px',
marginTop: '2em',
padding: '1em'
},
[`& .${classes.pataje}`]: {
position: 'absolute',
zIndex: 9999
},
[`& .${classes.separation}`]: {
color: 'black',
...theme.applyStyles('dark', {
color: 'white'
})
},
'@media (max-width: 600px)': {
marginLeft: '0',
},
'@media (min-width: 600px)': {
marginLeft: '240px',
},
}))
const LANG_NAMES = {
fr: 'Français', en: 'English', es: 'Español', de: 'Deutsch', it: 'Italiano',
pt: 'Português', ja: '日本語', ko: '한국어',
}
const CONTINENTS = ['Afrique', 'Asie', 'Europe']
const TRAD_FIELDS = [
// Afrique — langues vedettes en premier
{field: 'yoruba', title: 'Yorùbá', continent: 'Afrique', vedette: true},
{field: 'lingala', title: 'Lingála', continent: 'Afrique', vedette: true},
{field: 'wolof', title: 'Wolof', continent: 'Afrique', vedette: true},
{field: 'swahili', title: 'Kiswahili', continent: 'Afrique', vedette: true},
{field: 'hausa', title: 'Hausa', continent: 'Afrique', vedette: true},
{field: 'arabe', title: 'العربية', continent: 'Afrique'},
{field: 'oromo', title: 'Oromoo', continent: 'Afrique'},
{field: 'igbo', title: 'Igbo', continent: 'Afrique'},
{field: 'zoulou', title: 'isiZulu', continent: 'Afrique'},
{field: 'malgache', title: 'Malagasy', continent: 'Afrique'},
{field: 'xhosa', title: 'isiXhosa', continent: 'Afrique'},
{field: 'tswana', title: 'Setswana', continent: 'Afrique'},
{field: 'tsonga', title: 'Xitsonga', continent: 'Afrique'},
{field: 'sesotho', title: 'Sesotho', continent: 'Afrique'},
// Asie
{field: 'japonais', title: '日本語', continent: 'Asie'},
{field: 'coreen', title: '한국어', continent: 'Asie'},
// Europe
{field: 'anglais', title: 'English', continent: 'Europe'},
{field: 'francais', title: 'Français', continent: 'Europe'},
{field: 'espagnol', title: 'Español', continent: 'Europe'},
{field: 'allemand', title: 'Deutsch', continent: 'Europe'},
{field: 'italien', title: 'Italiano', continent: 'Europe'},
{field: 'portugais', title: 'Português', continent: 'Europe'},
]
const langToArray = parole => {
if (!parole || !parole.traductions) {
return []
}
return TRAD_FIELDS
.filter(({field}) => parole.traductions[field])
.map(({field, title, continent, vedette}) => ({field, title, continent, vedette, lang: parole.traductions[field]}))
}
const ExplicitTooltip = Tooltip
export default function Teks({parole}) {
const isMobile = useMediaQuery('(max-width:600px)')
const langArray = langToArray(parole)
const enhancedAliases = getAlias(parole.artistes, parole.prioriteArtistes, true)
const coverFmt = formatKuveti(parole.couverture)
const [tab, setTab] = useState(0)
const [, setCurrentTrack] = useCurrentTrack()
useEffect(() => {
const isBrowser = () => typeof window !== 'undefined'
if (!isBrowser()) {
return
}
window.scrollTo({top: 0, behavior: 'smooth'})
}, [])
useEffect(() => {
setCurrentTrack(parole)
return () => setCurrentTrack(null)
}, [parole, setCurrentTrack])
return (
<Root className={classes.container}>
<Box sx={{textAlign: 'center', marginTop: 12}}>
<Typography variant='h4' component='div' display='block' marginbottom={2}>
<Typography gutterBottom color='primary' variant='h6' component='div'>
{enhancedAliases.map(({type, value}) => {
if (type === 'element') {
return (
<Link key={value} style={{textDecoration: 'none', color: 'inherit'}} href={`/awtis/${slugify(value, {lower: true, remove: /[*#+~.()'"!:@]/g})}`}>{value}</Link>
)
}
return <span key={value} className={classes.separation}>{value}</span>
})}
</Typography>
<Box display='flex' justifycontent='center' alignitems='center'>
<Typography variant='h5' component='div'>
{parole.titre} <small>({parole?.annee})</small>
</Typography>
{parole.explicitLyrics && (
<ExplicitTooltip
title='Explicit Lyrics'
placement='bottom'
classes={{
tooltip: classes.tooltip
}}
>
<ExplicitIcon style={{marginLeft: 10}} color='error' />
</ExplicitTooltip>
)}
</Box>
</Typography>
{coverFmt?.url && (
<Box sx={{display: 'flex', justifyContent: 'center', mb: 2}}>
<Image
src={new URL(coverFmt.url, IMAGE_URL).toString()}
alt={parole.titre}
width={coverFmt.width || 300}
height={coverFmt.height || 300}
style={{maxWidth: '100%', maxHeight: 320, width: 'auto', height: 'auto', borderRadius: 8}}
/>
</Box>
)}
{parole?.user && (
<Typography style={{marginBottom: '1.5em'}} display='block' variant='caption'>
<i>parole soumise par {parole.user.username}</i>
</Typography>
)}
{parole?.userAdmin && !parole.user && (
<Typography style={{marginBottom: '1.5em'}} display='block' variant='caption'>
<i>parole soumise par {parole.userAdmin.firstname}</i>
</Typography>
)}
{parole.creativeCommons && (
<Box sx={{maxWidth: 220, margin: '0 auto', textAlign: 'center'}}>
<LicenseModal license={parole.creativeCommons.toLowerCase()} sourceOriginale={parole.sourceOriginale ?? null} remixes={parole.remixes?.length ? parole.remixes : null} />
</Box>
)}
{(parole?.files || (parole.creativeCommons && parole.kits?.length > 0)) && (
<Box sx={{display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 1}}>
{parole?.files && (
<FilesDialog files={parole.files} />
)}
{parole.creativeCommons && parole.kits?.length > 0 && (
<KitsDialog kits={parole.kits} />
)}
</Box>
)}
{(parole.okiMizikID || parole.streamAudio.length > 0 || parole.gadeEmbed) && (
<Box sx={{textAlign: 'center'}}>
<EntegreMizik parole={parole} isMobile={isMobile} />
</Box>
)}
{parole.okiMizikID && (
<OkiMizik id={parole.okiMizikID} parole={parole} />
)}
{parole?.difference?.length > 0 && (
<DiferansDialog difference={parole.difference} />
)}
{parole.streamAudio?.length > 0 && (
<Box sx={{mt: 2, mb: 1}}>
<Typography variant='overline' sx={{color: 'text.secondary', display: 'block', mb: 1}}>
Écouter sur
</Typography>
<Box sx={{display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 1}}>
{parole.streamAudio.map(lyen => (
<StreamButton key={lyen.url} lyen={lyen} />
))}
</Box>
</Box>
)}
</Box>
<Box sx={{maxWidth: 700, margin: '0 auto'}} className={classes.gridText}>
<Box sx={{display: 'flex', justifyContent: 'center', borderBottom: 1, borderColor: 'divider', mb: 2, pb: 2}}>
<FormControl size='small' sx={{minWidth: 240, width: isMobile ? '100%' : 'auto'}}>
<Select
value={tab}
MenuProps={{PaperProps: {sx: {maxHeight: 420}}}}
renderValue={value => {
const label = value === 0 ? 'Transcription' : langArray[value - 1]?.title
const Icon = value === 0 ? ArticleIcon : TranslateIcon
return (
<Box sx={{display: 'flex', alignItems: 'center', gap: 1, fontWeight: value === 0 ? 700 : 400}}>
<Icon fontSize='small' color='primary' />
{label}
</Box>
)
}}
onChange={event => setTab(event.target.value)}
>
<MenuItem value={0} sx={{display: 'flex', gap: 0.75, fontWeight: 700}}>
<ArticleIcon fontSize='inherit' color='primary' />
Transcription
</MenuItem>
<Divider />
{CONTINENTS.flatMap(continent => {
const items = langArray
.map((item, index) => ({...item, index}))
.filter(item => item.continent === continent)
if (items.length === 0) {
return []
}
return [
<ListSubheader
key={`entet-${continent}`}
sx={{
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: 1,
color: 'primary.main',
lineHeight: '2.5em'
}}
>
{continent}
</ListSubheader>,
...items.map(({title, index, vedette}) => (
<MenuItem key={title} value={index + 1} sx={{display: 'flex', gap: 0.75}}>
{vedette && <StarIcon fontSize='inherit' color='primary' />}
{title}
</MenuItem>
))
]
})}
</Select>
</FormControl>
</Box>
{tab === 0 && (
<>
{parole.langueSource && parole.langueSource !== 'ka' && (
<Typography align='center' variant='body2' color='text.secondary' sx={{mb: 1}}>
({LANG_NAMES[parole.langueSource]})
</Typography>
)}
<Typography paragraph='true' align={isMobile ? 'justify' : 'center'} component='span'>
{formatJsonString(parole.transcription)}
</Typography>
</>
)}
{langArray.map(({title, lang}, index) => (
tab === index + 1 && (
<Typography key={title} paragraph='true' align='justify' component='span'>
{formatJsonString(lang)}
</Typography>
)
))}
</Box>
<ReponsMastodon bokanteStatusId={parole.bokanteStatusId} />
<Komante commentaires={parole.commentaires} />
</Root>
)
}
Teks.propTypes = {
parole: PropTypes.object.isRequired,
}