audio hotfixies (#126)

* - audio hotfixies

* refactoring: audio player

* Update volume control styles

* Artist Data conditional render

* Update composeMediaItems title with remove File Extension

---------

Co-authored-by: bniwredyc <bniwredyc@gmail.com>
This commit is contained in:
Ilya Y 2023-07-18 14:26:32 +03:00 committed by GitHub
parent e66eeb48df
commit be9fea1265
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 211 additions and 297 deletions

View File

@ -49,7 +49,7 @@
width: 200px;
height: 200px;
transition: all 0.2s ease-in-out;
background: var(--placeholder-color-semi) url('../../icons/create-music.svg') no-repeat 50% 50%;
background: var(--placeholder-color-semi) url('/icons/create-music.svg') no-repeat 50% 50%;
.image {
object-fit: cover;

View File

@ -36,17 +36,19 @@ export const AudioHeader = (props: Props) => {
</div>
</Show>
<h1>{props.title}</h1>
<div class={styles.artistData}>
<Show when={props.artistData.artist}>
<div class={styles.item}>{props.artistData.artist}</div>
</Show>
<Show when={props.artistData.date}>
<div class={styles.item}>{props.artistData.date}</div>
</Show>
<Show when={props.artistData.genre}>
<div class={styles.item}>{props.artistData.genre}</div>
</Show>
</div>
<Show when={props.artistData}>
<div class={styles.artistData}>
<Show when={props.artistData?.artist}>
<div class={styles.item}>{props.artistData.artist}</div>
</Show>
<Show when={props.artistData?.date}>
<div class={styles.item}>{props.artistData.date}</div>
</Show>
<Show when={props.artistData?.genre}>
<div class={styles.item}>{props.artistData.genre}</div>
</Show>
</div>
</Show>
</div>
</div>
)

View File

@ -153,75 +153,56 @@
align-items: center;
}
$vendors-track: ('::-webkit-slider-runnable-track', '::-moz-range-track', '::-ms-track');
$vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thumb');
.volume {
position: absolute;
z-index: 2;
right: 0;
bottom: 14px;
height: 28px;
-webkit-appearance: none;
margin: 10px 0;
width: 80px;
background: transparent;
height: 19px;
float: left;
outline: none;
border: 2px solid black;
padding: 16px 8px;
position: absolute;
bottom: 60px;
left: -21px;
width: 100px;
transform: rotate(-90deg);
background: var(--background-color);
&:focus {
outline: none;
@each $vendor in $vendors-track {
&#{$vendor} {
width: 100%;
height: 3px;
cursor: pointer;
background: var(--background-color-invert);
}
}
&::-moz-range-thumb,
&::-webkit-slider-thumb {
height: 16px;
width: 16px;
border-radius: 100px;
border: none;
cursor: pointer;
margin-top: -4px;
}
&:focus::-webkit-slider-runnable-track,
&::-moz-range-thumb,
&::-webkit-slider-thumb,
&::-webkit-slider-runnable-track,
&::-moz-range-track,
&::-ms-track,
&::-ms-fill-lower,
&::-ms-fill-upper,
&::-ms-thumb {
background: #0e0e0e;
@each $vendor in $vendors-thumb {
&#{$vendor} {
position: relative;
-webkit-appearance: none;
box-sizing: content-box;
width: 8px;
height: 8px;
border-radius: 50%;
border: 4px solid var(--default-color);
background-color: var(--background-color);
cursor: pointer;
margin: -7px 0 0 0;
}
&:active#{$vendor} {
transform: scale(1.2);
background: var(--background-color);
}
}
&::-webkit-slider-thumb {
-webkit-appearance: none;
&::-moz-range-progress {
background-color: var(--background-color);
}
&::-webkit-slider-runnable-track,
&::-moz-range-track,
&::-ms-track {
width: 100%;
height: 6px;
cursor: pointer;
animate: 0.2s;
border-radius: 10px;
}
&::-ms-fill-lower,
&::-ms-fill-upper {
border-radius: 10px;
}
&::-ms-thumb {
margin-top: 1px;
height: 15px;
width: 15px;
border-radius: 5px;
border: none;
cursor: pointer;
}
&:focus::-ms-fill-lower,
&:focus::-ms-fill-upper {
background: #38bdf8;
&::-moz-focus-outer {
border: 0;
}
}

View File

@ -1,236 +1,170 @@
import { createEffect, createSignal, on, onMount, Show } from 'solid-js'
import { createEffect, createMemo, createSignal, on, onMount, Show } from 'solid-js'
import { PlayerHeader } from './PlayerHeader'
import { PlayerPlaylist } from './PlayerPlaylist'
import styles from './AudioPlayer.module.scss'
import { MediaItem } from '../../../pages/types'
import { imageProxy } from '../../../utils/imageProxy'
export type Audio = {
pic?: string
index?: number
isCurrent?: boolean
isPlaying?: boolean
} & MediaItem
type Props = {
media: Audio[]
media: MediaItem[]
articleSlug?: string
body?: string
editorMode?: boolean
onAudioChange?: (index: number, field: string, value: string) => void
}
const prepareMedia = (media: Audio[]) =>
media.map((item, index) => ({
...item,
url: imageProxy(item.url),
index: index,
isCurrent: false,
isPlaying: false
}))
const progressUpdate = (audioRef, progressFilledRef, duration) => {
progressFilledRef.current.style.width = `${(audioRef.current.currentTime / duration) * 100 || 0}%`
}
const scrub = (event, progressRef, duration, audioRef) => {
audioRef.current.currentTime = (event.offsetX / progressRef.current.offsetWidth) * duration
onMediaItemFieldChange?: (index: number, field: keyof MediaItem, value: string) => void
}
const getFormattedTime = (point) => new Date(point * 1000).toISOString().slice(14, -5)
export const AudioPlayer = (props: Props) => {
const audioRef: { current: HTMLAudioElement } = { current: null }
const gainNodeRef: { current: GainNode } = { current: null }
const progressRef: { current: HTMLDivElement } = { current: null }
const progressFilledRef: { current: HTMLDivElement } = { current: null }
const audioContextRef: { current: AudioContext } = { current: null }
const mouseDownRef: { current: boolean } = { current: false }
const [audioContext, setAudioContext] = createSignal<AudioContext>()
const [gainNode, setGainNode] = createSignal<GainNode>()
const [tracks, setTracks] = createSignal<Audio[] | null>(prepareMedia(props.media))
const [duration, setDuration] = createSignal<number>(0)
const [currentTimeContent, setCurrentTimeContent] = createSignal<string>('00:00')
const [currentDurationContent, setCurrentDurationContent] = createSignal<string>('00:00')
const [mousedown, setMousedown] = createSignal<boolean>(false)
const [currentTrackDuration, setCurrentTrackDuration] = createSignal(0)
const [currentTime, setCurrentTime] = createSignal(0)
const [currentTrackIndex, setCurrentTrackIndex] = createSignal<number>(0)
const [isPlaying, setIsPlaying] = createSignal(false)
const currentTack = createMemo(() => props.media[currentTrackIndex()])
createEffect(
on(
() => props.media,
() => currentTrackIndex(),
() => {
setTracks(prepareMedia(props.media))
setCurrentTrackDuration(0)
}
)
)
const getCurrentTrack = () =>
tracks().find((track) => track.isCurrent) ||
(() => {
setTracks(
tracks().map((track, index) => ({
...track,
isCurrent: index === 0
}))
)
return tracks()[0]
})()
createEffect(() => {
if (audioRef.current.src !== getCurrentTrack().url) {
audioRef.current.src = getCurrentTrack().url
const handlePlayMedia = async (trackIndex: number) => {
setIsPlaying(!isPlaying() || trackIndex !== currentTrackIndex())
setCurrentTrackIndex(trackIndex)
audioRef.current.load()
if (audioContextRef.current.state === 'suspended') {
await audioContextRef.current.resume()
}
})
createEffect(() => {
if (getCurrentTrack() && duration()) {
setCurrentDurationContent(getFormattedTime(duration()))
}
})
const playMedia = async (m: Audio) => {
setTracks(
tracks().map((track) => ({
...track,
isCurrent: track.index === m.index,
isPlaying: track.index === m.index ? !track.isPlaying : false
}))
)
progressUpdate(audioRef, progressFilledRef, duration())
if (audioContext().state === 'suspended') await audioContext().resume()
if (getCurrentTrack().isPlaying) {
if (isPlaying()) {
await audioRef.current.play()
} else {
audioRef.current.pause()
}
}
const setTimes = () => {
setCurrentTimeContent(getFormattedTime(audioRef.current.currentTime))
const handleVolumeChange = (volume: number) => {
gainNodeRef.current.gain.value = volume
}
const handleAudioEnd = () => {
progressFilledRef.current.style.width = '0%'
if (currentTrackIndex() < props.media.length - 1) {
playNextTrack()
return
}
audioRef.current.currentTime = 0
setIsPlaying(false)
setCurrentTrackIndex(0)
}
const handleAudioTimeUpdate = () => {
progressUpdate(audioRef, progressFilledRef, duration())
setTimes()
setCurrentTime(audioRef.current.currentTime)
}
onMount(() => {
setAudioContext(new AudioContext())
setGainNode(audioContext().createGain())
audioContextRef.current = new AudioContext()
gainNodeRef.current = audioContextRef.current.createGain()
setTimes()
const track = audioContext().createMediaElementSource(audioRef.current)
track.connect(gainNode()).connect(audioContext().destination)
const track = audioContextRef.current.createMediaElementSource(audioRef.current)
track.connect(gainNodeRef.current).connect(audioContextRef.current.destination)
})
const playPrevTrack = () => {
const { index } = getCurrentTrack()
const currIndex = tracks().findIndex((track) => track.index === index)
let newCurrentTrackIndex = currentTrackIndex() - 1
if (newCurrentTrackIndex < 0) {
newCurrentTrackIndex = 0
}
const getUpdatedStatus = (trackId) =>
currIndex === 0
? trackId === tracks()[tracks().length - 1].index
: trackId === tracks()[currIndex - 1].index
setTracks(
tracks().map((track) => ({
...track,
isCurrent: getUpdatedStatus(track.index),
isPlaying: getUpdatedStatus(track.index)
}))
)
setCurrentTrackIndex(newCurrentTrackIndex)
}
const playNextTrack = () => {
const { index } = getCurrentTrack()
const currIndex = tracks().findIndex((track) => track.index === index)
let newCurrentTrackIndex = currentTrackIndex() + 1
if (newCurrentTrackIndex > props.media.length - 1) {
newCurrentTrackIndex = props.media.length - 1
}
const getUpdatedStatus = (trackId) =>
currIndex === tracks().length - 1
? trackId === tracks()[0].index
: trackId === tracks()[currIndex + 1].index
setTracks(
tracks().map((track) => ({
...track,
isCurrent: getUpdatedStatus(track.index),
isPlaying: getUpdatedStatus(track.index)
}))
)
setCurrentTrackIndex(newCurrentTrackIndex)
}
const handleOnAudioMetadataLoad = ({ target }) => {
setDuration(target.duration)
const handleMediaItemFieldChange = (index: number, field: keyof MediaItem, value) => {
props.onMediaItemFieldChange(index, field, value)
}
const handleAudioDescriptionChange = (index: number, field: string, value) => {
props.onAudioChange(index, field, value)
setTracks(
tracks().map((track, idx) => {
return idx === index ? { ...track, [field]: value } : track
})
)
const scrub = (event) => {
audioRef.current.currentTime =
(event.offsetX / progressRef.current.offsetWidth) * currentTrackDuration()
}
return (
<div>
<Show when={getCurrentTrack()}>
<Show when={props.media}>
<PlayerHeader
onPlayMedia={() => playMedia(getCurrentTrack())}
getCurrentTrack={getCurrentTrack}
onPlayMedia={() => handlePlayMedia(currentTrackIndex())}
playPrevTrack={playPrevTrack}
playNextTrack={playNextTrack}
gainNode={gainNode()}
onVolumeChange={handleVolumeChange}
isPlaying={isPlaying()}
currentTrack={currentTack()}
/>
</Show>
<Show when={getCurrentTrack()}>
<div class={styles.timeline}>
<div
class={styles.progress}
ref={(el) => (progressRef.current = el)}
onClick={(e) => scrub(e, progressRef, duration(), audioRef)}
onMouseMove={(e) => mousedown() && scrub(e, progressRef, duration(), audioRef)}
onMouseDown={() => setMousedown(true)}
onMouseUp={() => setMousedown(false)}
onClick={(e) => scrub(e)}
onMouseMove={(e) => mouseDownRef.current && scrub(e)}
onMouseDown={() => (mouseDownRef.current = true)}
onMouseUp={() => (mouseDownRef.current = false)}
>
<div class={styles.progressFilled} ref={(el) => (progressFilledRef.current = el)} />
<div
class={styles.progressFilled}
style={{
width: `${(currentTime() / currentTrackDuration()) * 100 || 0}%`
}}
/>
</div>
<div class={styles.progressTiming}>
<span>{currentTimeContent()}</span>
<span>{currentDurationContent()}</span>
<span>{getFormattedTime(currentTime())}</span>
<Show when={currentTrackDuration() > 0}>
<span>{getFormattedTime(currentTrackDuration())}</span>
</Show>
</div>
<audio
ref={(el) => (audioRef.current = el)}
onTimeUpdate={handleAudioTimeUpdate}
// TEMP SOLUTION for http/https
src={currentTack().url.startsWith('https') ? currentTack().url : imageProxy(currentTack().url)}
onCanPlay={() => {
if (getCurrentTrack().isPlaying) {
// start to play the next track on src change
if (isPlaying()) {
audioRef.current.play()
}
}}
onLoadedMetadata={handleOnAudioMetadataLoad}
onLoadedMetadata={({ currentTarget }) => setCurrentTrackDuration(currentTarget.duration)}
onEnded={handleAudioEnd}
crossorigin="anonymous"
/>
</div>
</Show>
<Show when={tracks()}>
<PlayerPlaylist
editorMode={props.editorMode}
playMedia={playMedia}
tracks={tracks()}
currentTrack={getCurrentTrack()}
onPlayMedia={handlePlayMedia}
isPlaying={isPlaying()}
media={props.media}
currentTrackIndex={currentTrackIndex()}
articleSlug={props.articleSlug}
body={props.body}
onAudioChange={handleAudioDescriptionChange}
onMediaItemFieldChange={handleMediaItemFieldChange}
/>
</Show>
</div>

View File

@ -5,21 +5,24 @@ import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
import { Icon } from '../../_shared/Icon'
import styles from './AudioPlayer.module.scss'
import { MediaItem } from '../../../pages/types'
export const PlayerHeader = (props) => {
let volumeRef: HTMLInputElement
type Props = {
onPlayMedia: () => void
playPrevTrack: () => void
playNextTrack: () => void
onVolumeChange: (volume: number) => void
isPlaying: boolean
currentTrack: MediaItem
}
export const PlayerHeader = (props: Props) => {
const volumeContainerRef: { current: HTMLDivElement } = {
current: null
}
const { getCurrentTrack, onPlayMedia, gainNode, playPrevTrack, playNextTrack } = props
const [isVolumeBarOpened, setIsVolumeBarOpened] = createSignal(false)
const handleVolumeChange = () => {
gainNode.gain.value = Number(volumeRef.value)
}
const toggleVolumeBar = () => {
setIsVolumeBarOpened(!isVolumeBarOpened())
}
@ -32,23 +35,23 @@ export const PlayerHeader = (props) => {
return (
<div class={styles.playerHeader}>
<div class={styles.playerTitle}>{getCurrentTrack().title}</div>
<div class={styles.playerTitle}>{props.currentTrack.title}</div>
<div class={styles.playerControls}>
<button
type="button"
onClick={onPlayMedia}
onClick={props.onPlayMedia}
class={clsx(
styles.playButton,
getCurrentTrack().isPlaying ? styles.playButtonInvertPause : styles.playButtonInvertPlay
props.isPlaying ? styles.playButtonInvertPause : styles.playButtonInvertPlay
)}
aria-label="Play"
data-playing="false"
>
<Icon name={getCurrentTrack().isPlaying ? 'pause' : 'play'} />
<Icon name={props.isPlaying ? 'pause' : 'play'} />
</button>
<button
type="button"
onClick={playPrevTrack}
onClick={props.playPrevTrack}
class={clsx(styles.controlsButton)}
aria-label="Previous"
>
@ -56,7 +59,7 @@ export const PlayerHeader = (props) => {
</button>
<button
type="button"
onClick={playNextTrack}
onClick={props.playNextTrack}
class={clsx(styles.controlsButton, styles.controlsButtonNext)}
aria-label="Next"
>
@ -65,7 +68,6 @@ export const PlayerHeader = (props) => {
<div ref={(el) => (volumeContainerRef.current = el)} class={styles.volumeContainer}>
<Show when={isVolumeBarOpened()}>
<input
ref={volumeRef}
type="range"
id="volume"
min="0"
@ -73,7 +75,7 @@ export const PlayerHeader = (props) => {
value="1"
step="0.01"
class={styles.volume}
onChange={handleVolumeChange}
onChange={({ target }) => props.onVolumeChange(Number(target.value))}
/>
</Show>
<button onClick={toggleVolumeBar} class={styles.volumeButton} role="button" aria-label="Volume">

View File

@ -2,21 +2,22 @@ import { createSignal, For, Show } from 'solid-js'
import { SharePopup, getShareUrl } from '../SharePopup'
import { getDescription } from '../../../utils/meta'
import { useLocalize } from '../../../context/localize'
import type { Audio } from './AudioPlayer'
import { Popover } from '../../_shared/Popover'
import { Icon } from '../../_shared/Icon'
import styles from './AudioPlayer.module.scss'
import { GrowingTextarea } from '../../_shared/GrowingTextarea'
import MD from '../MD'
import { MediaItem } from '../../../pages/types'
type Props = {
tracks: Audio[]
currentTrack: Audio
playMedia: (audio: Audio) => void
media: MediaItem[]
currentTrackIndex: number
isPlaying: boolean
onPlayMedia: (trackIndex: number) => void
articleSlug?: string
body?: string
editorMode?: boolean
onAudioChange?: (index: number, field: string, value: string) => void
onMediaItemFieldChange?: (index: number, field: keyof MediaItem, value: string) => void
}
export const PlayerPlaylist = (props: Props) => {
@ -26,62 +27,52 @@ export const PlayerPlaylist = (props: Props) => {
const toggleDropDown = (index) => {
setActiveEditIndex(activeEditIndex() === index ? -1 : index)
}
const updateData = (key, value) => {
props.onAudioChange(activeEditIndex(), key, value)
const handleMediaItemFieldChange = (field: keyof MediaItem, value: string) => {
props.onMediaItemFieldChange(activeEditIndex(), field, value)
}
return (
<ul class={styles.playlist}>
<For each={props.tracks}>
{(m: Audio, index) => (
<For each={props.media}>
{(mi, index) => (
<li>
<div class={styles.playlistItem}>
<button
class={styles.playlistItemPlayButton}
onClick={() => props.playMedia(m)}
onClick={() => props.onPlayMedia(index())}
type="button"
aria-label="Play"
>
<Icon
name={
props.currentTrack &&
props.currentTrack.index === m.index &&
props.currentTrack.isPlaying
? 'pause'
: 'play'
}
/>
<Icon name={props.currentTrackIndex === index() && props.isPlaying ? 'pause' : 'play'} />
</button>
<div class={styles.playlistItemText}>
<Show
when={activeEditIndex() === index() && props.editorMode}
fallback={
<>
<div class={styles.title}>
{m.title.replace(/\.(wav|flac|mp3|aac)$/i, '') || t('Song title')}
</div>
<div class={styles.artist}>{m.artist || t('Artist')}</div>
<div class={styles.title}>{mi.title || t('Song title')}</div>
<div class={styles.artist}>{mi.artist || t('Artist')}</div>
</>
}
>
<input
type="text"
value={m.title}
value={mi.title}
class={styles.title}
placeholder={t('Song title')}
onChange={(e) => updateData('title', e.target.value)}
onChange={(e) => handleMediaItemFieldChange('title', e.target.value)}
/>
<input
type="text"
value={m.artist}
value={mi.artist}
class={styles.artist}
placeholder={t('Artist')}
onChange={(e) => updateData('artist', e.target.value)}
onChange={(e) => handleMediaItemFieldChange('artist', e.target.value)}
/>
</Show>
</div>
<div class={styles.actions}>
<Show when={(m.lyrics || m.body) && !props.editorMode}>
<Show when={(mi.lyrics || mi.body) && !props.editorMode}>
<Popover content={t('Show lyrics')}>
{(triggerRef: (el) => void) => (
<button ref={triggerRef} type="button" onClick={() => toggleDropDown(index())}>
@ -102,9 +93,9 @@ export const PlayerPlaylist = (props: Props) => {
}
>
<SharePopup
title={m.title}
title={mi.title}
description={getDescription(props.body)}
imageUrl={m.pic}
imageUrl={mi.pic}
shareUrl={getShareUrl({ pathname: `/${props.articleSlug}` })}
trigger={
<div>
@ -123,14 +114,15 @@ export const PlayerPlaylist = (props: Props) => {
when={props.editorMode}
fallback={
<div class={styles.descriptionBlock}>
<Show when={m.body}>
<Show when={mi.body}>
<div class={styles.description}>
<MD body={m.body} />
{/*FIXME*/}
<MD body={mi.body} />
</div>
</Show>
<Show when={m.lyrics}>
<Show when={mi.lyrics}>
<div class={styles.lyrics}>
<MD body={m.lyrics} />
<MD body={mi.lyrics} />
</div>
</Show>
</div>
@ -141,15 +133,15 @@ export const PlayerPlaylist = (props: Props) => {
allowEnterKey={true}
class={styles.description}
placeholder={t('Description')}
value={(value) => updateData('body', value)}
initialValue={m.body || ''}
value={(value) => handleMediaItemFieldChange('body', value)}
initialValue={mi.body || ''}
/>
<GrowingTextarea
allowEnterKey={true}
class={styles.lyrics}
placeholder={t('Song lyrics')}
value={(value) => updateData('lyrics', value)}
initialValue={m.lyrics || ''}
value={(value) => handleMediaItemFieldChange('lyrics', value)}
initialValue={mi.lyrics || ''}
/>
</div>
</Show>

View File

@ -189,6 +189,13 @@ export const FullArticle = (props: ArticleProps) => {
</For>
</div>
</Show>
<Show when={media().length > 0 && props.article.layout === 'audio'}>
<div class="media-items">
<AudioPlayer media={media()} articleSlug={props.article.slug} body={body()} />
</div>
</Show>
<Show when={body()}>
<div class={styles.shoutBody}>
<Show when={!body().startsWith('<')} fallback={<div innerHTML={body()} />}>

View File

@ -1,11 +1,6 @@
.rating {
align-items: center;
display: flex;
.ratingControl {
&:hover {
}
}
}
.ratingValue {

View File

@ -1,4 +1,3 @@
import styles from './ShoutRatingControl.module.scss'
import { clsx } from 'clsx'
import { createMemo, Show } from 'solid-js'
import { ReactionKind, Shout } from '../../graphql/types.gen'
@ -9,6 +8,7 @@ import { Popup } from '../_shared/Popup'
import { VotersList } from '../_shared/VotersList'
import { useLocalize } from '../../context/localize'
import { Icon } from '../_shared/Icon'
import styles from './ShoutRatingControl.module.scss'
interface ShoutRatingControlProps {
shout: Shout
@ -82,7 +82,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
return (
<div class={clsx(styles.rating, props.class)}>
<button class={styles.ratingControl} onClick={() => handleRatingChange(false)}>
<button onClick={() => handleRatingChange(false)}>
<Show when={!isDownvoted()}>
<Icon name="rating-control-less" />
</Show>
@ -98,7 +98,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
/>
</Popup>
<button class={styles.ratingControl} onClick={() => handleRatingChange(true)}>
<button onClick={() => handleRatingChange(true)}>
<Show when={!isUpvoted()}>
<Icon name="rating-control-more" />
</Show>

View File

@ -25,14 +25,18 @@ type Props = {
export const AudioUploader = (props: Props) => {
const { t } = useLocalize()
const handleAudioDescriptionChange = (index: number, field: string, value) => {
const handleMediaItemFieldChange = (index: number, field: keyof MediaItem, value) => {
props.onAudioChange(index, { ...props.audio[index], [field]: value })
}
return (
<div class={clsx(styles.AudioUploader, props.class)}>
<Show when={props.audio.length > 0}>
<AudioPlayer editorMode={true} media={props.audio} onAudioChange={handleAudioDescriptionChange} />
<AudioPlayer
editorMode={true}
media={props.audio}
onMediaItemFieldChange={handleMediaItemFieldChange}
/>
</Show>
<DropArea
isMultiply={true}

View File

@ -25,31 +25,24 @@ export type RootSearchParams = {
lang: string
}
export type UploadFile = {
source: string
name: string
size: number
file: File
}
export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature'
export type FileTypeToUpload = 'image' | 'video' | 'doc' | 'audio'
export type AudioDescription = {
export type MediaItem = {
url: string
title: string
body: string
source?: string // for image
pic?: string
// audio specific properties
date?: string
genre?: string
artist?: string
lyrics?: string
}
export type MediaItem = {
url: string
title: string
body: string
source?: string
} & AudioDescription
export type UploadedFile = {
url: string
originalFilename: string

View File

@ -1,11 +1,15 @@
import { UploadedFile } from '../pages/types'
const removeFileExtension = (fileName: string) => {
return fileName.replace(/\.(wav|flac|mp3|aac|jpg|jpeg|png|gif)$/i, '')
}
export const composeMediaItems = (value: UploadedFile[], optionalParams = {}) => {
return value.map((fileData) => {
return {
url: fileData.url,
source: '',
title: fileData.originalFilename,
title: removeFileExtension(fileData.originalFilename),
body: '',
...optionalParams
}