refacor video upload (#138)
* refacor video upload * remove unused imports
This commit is contained in:
parent
8a2f8d2f98
commit
664adfc2dc
|
@ -180,7 +180,12 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
<For each={media() || []}>
|
<For each={media() || []}>
|
||||||
{(m: MediaItem) => (
|
{(m: MediaItem) => (
|
||||||
<div class={styles.shoutMediaBody}>
|
<div class={styles.shoutMediaBody}>
|
||||||
<VideoPlayer videoUrl={m.url} title={m.title} description={m.body} />
|
<VideoPlayer
|
||||||
|
articleView={true}
|
||||||
|
videoUrl={m.url}
|
||||||
|
title={m.title}
|
||||||
|
description={m.body}
|
||||||
|
/>
|
||||||
<Show when={m?.body}>
|
<Show when={m?.body}>
|
||||||
<MD body={m.body} />
|
<MD body={m.body} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|
|
@ -2,32 +2,24 @@ import { clsx } from 'clsx'
|
||||||
import styles from './VideoUploader.module.scss'
|
import styles from './VideoUploader.module.scss'
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
import { createDropzone } from '@solid-primitives/upload'
|
import { createDropzone } from '@solid-primitives/upload'
|
||||||
import { createEffect, createSignal, Show } from 'solid-js'
|
import { createSignal, For, Show } from 'solid-js'
|
||||||
import { useSnackbar } from '../../../context/snackbar'
|
import { useSnackbar } from '../../../context/snackbar'
|
||||||
import { validateUrl } from '../../../utils/validateUrl'
|
import { validateUrl } from '../../../utils/validateUrl'
|
||||||
import { VideoPlayer } from '../../_shared/VideoPlayer'
|
|
||||||
import type { MediaItem } from '../../../pages/types'
|
import type { MediaItem } from '../../../pages/types'
|
||||||
// import { handleFileUpload } from '../../../utils/handleFileUpload'
|
import { composeMediaItems } from '../../../utils/composeMediaItems'
|
||||||
|
import { VideoPlayer } from '../../_shared/VideoPlayer'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string
|
video: MediaItem[]
|
||||||
data: (value: MediaItem[]) => void
|
onVideoAdd: (value: MediaItem[]) => void
|
||||||
|
onVideoDelete: (mediaItemIndex: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VideoUploader = (props: Props) => {
|
export const VideoUploader = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const [dragActive, setDragActive] = createSignal(false)
|
const [dragActive, setDragActive] = createSignal(false)
|
||||||
const [dragError, setDragError] = createSignal<string>()
|
const [error, setError] = createSignal<string>()
|
||||||
const [incorrectUrl, setIncorrectUrl] = createSignal<boolean>(false)
|
const [incorrectUrl, setIncorrectUrl] = createSignal<boolean>(false)
|
||||||
const [data, setData] = createSignal<MediaItem>()
|
|
||||||
|
|
||||||
const updateData = (key, value) => {
|
|
||||||
setData((prev) => ({ ...prev, [key]: value }))
|
|
||||||
}
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
props.data([data()])
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
actions: { showSnackbar }
|
actions: { showSnackbar }
|
||||||
|
@ -39,97 +31,84 @@ export const VideoUploader = (props: Props) => {
|
||||||
current: null
|
current: null
|
||||||
}
|
}
|
||||||
|
|
||||||
// const [videoUrl, setVideoUrl] = createSignal<string | undefined>()
|
|
||||||
// const runUpload = async (file) => {
|
|
||||||
// try {
|
|
||||||
// const fileUrl = await handleFileUpload(file)
|
|
||||||
// setVideoUrl(fileUrl)
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error('[runUpload]', error)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
const { setRef: dropzoneRef, files: droppedFiles } = createDropzone({
|
const { setRef: dropzoneRef, files: droppedFiles } = createDropzone({
|
||||||
onDrop: async () => {
|
onDrop: async () => {
|
||||||
setDragActive(false)
|
setDragActive(false)
|
||||||
if (droppedFiles().length > 1) {
|
if (droppedFiles().length > 1) {
|
||||||
setDragError(t('Many files, choose only one'))
|
setError(t('Many files, choose only one'))
|
||||||
} else if (droppedFiles()[0].file.type.startsWith('video/')) {
|
} else if (droppedFiles()[0].file.type.startsWith('video/')) {
|
||||||
await showSnackbar({
|
await showSnackbar({
|
||||||
body: t(
|
body: t(
|
||||||
'This functionality is currently not available, we would like to work on this issue. Use the download link.'
|
'This functionality is currently not available, we would like to work on this issue. Use the download link.'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
// await runUpload(droppedFiles()[0])
|
|
||||||
} else {
|
} else {
|
||||||
setDragError(t('Video format not supported'))
|
setError(t('Video format not supported'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const handleDrag = (event) => {
|
const handleDrag = (event) => {
|
||||||
if (event.type === 'dragenter' || event.type === 'dragover') {
|
if (event.type === 'dragenter' || event.type === 'dragover') {
|
||||||
setDragActive(true)
|
setDragActive(true)
|
||||||
setDragError()
|
setError()
|
||||||
} else if (event.type === 'dragleave') {
|
} else if (event.type === 'dragleave') {
|
||||||
setDragActive(false)
|
setDragActive(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUrlInput = async (value: string) => {
|
const handleUrlInput = async (value: string) => {
|
||||||
|
setError()
|
||||||
if (validateUrl(value)) {
|
if (validateUrl(value)) {
|
||||||
updateData('url', value)
|
props.onVideoAdd(composeMediaItems([{ url: value }]))
|
||||||
} else {
|
} else {
|
||||||
setIncorrectUrl(true)
|
setIncorrectUrl(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={clsx(styles.VideoUploader, props.class)}>
|
<Show
|
||||||
<Show
|
when={props.video.length === 0}
|
||||||
when={data() && data().url}
|
fallback={
|
||||||
fallback={
|
<For each={props.video}>
|
||||||
<>
|
{(mi, index) => (
|
||||||
<div
|
<VideoPlayer onVideoDelete={() => props.onVideoDelete(index())} videoUrl={mi?.url} />
|
||||||
onDragEnter={handleDrag}
|
)}
|
||||||
onDragLeave={handleDrag}
|
</For>
|
||||||
onDragOver={handleDrag}
|
}
|
||||||
onClick={() =>
|
>
|
||||||
showSnackbar({
|
<div class={styles.VideoUploader}>
|
||||||
body: t(
|
<div
|
||||||
'This functionality is currently not available, we would like to work on this issue. Use the download link.'
|
onDragEnter={handleDrag}
|
||||||
)
|
onDragLeave={handleDrag}
|
||||||
})
|
onDragOver={handleDrag}
|
||||||
}
|
onClick={() =>
|
||||||
ref={dropzoneRef}
|
showSnackbar({
|
||||||
class={clsx(styles.dropArea, { [styles.active]: dragActive() })}
|
body: t(
|
||||||
>
|
'This functionality is currently not available, we would like to work on this issue. Use the download link.'
|
||||||
<div class={styles.text}>{t('Upload video')}</div>
|
)
|
||||||
</div>
|
})
|
||||||
<Show when={dragError()}>
|
}
|
||||||
<div class={styles.error}>{dragError()}</div>
|
ref={dropzoneRef}
|
||||||
</Show>
|
class={clsx(styles.dropArea, { [styles.active]: dragActive() })}
|
||||||
<div class={styles.inputHolder}>
|
>
|
||||||
<input
|
<div class={styles.text}>{t('Upload video')}</div>
|
||||||
class={clsx(styles.urlInput, { [styles.hasError]: incorrectUrl() })}
|
</div>
|
||||||
ref={(el) => (urlInput.current = el)}
|
<Show when={error()}>
|
||||||
type="text"
|
<div class={styles.error}>{error()}</div>
|
||||||
placeholder={t('Insert video link')}
|
</Show>
|
||||||
onChange={(event) => handleUrlInput(event.currentTarget.value)}
|
<div class={styles.inputHolder}>
|
||||||
/>
|
<input
|
||||||
</div>
|
class={clsx(styles.urlInput, { [styles.hasError]: incorrectUrl() })}
|
||||||
<Show when={incorrectUrl()}>
|
ref={(el) => (urlInput.current = el)}
|
||||||
<div class={styles.error}>{t('It does not look like url')}</div>
|
type="text"
|
||||||
</Show>
|
placeholder={t('Insert video link')}
|
||||||
</>
|
onChange={(event) => handleUrlInput(event.currentTarget.value)}
|
||||||
}
|
/>
|
||||||
>
|
</div>
|
||||||
<VideoPlayer
|
<Show when={incorrectUrl()}>
|
||||||
deleteAction={() => setData()}
|
<div class={styles.error}>{t('It does not look like url')}</div>
|
||||||
videoUrl={data().url}
|
</Show>
|
||||||
title={data().title}
|
</div>
|
||||||
description={data().body}
|
</Show>
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { For, Show, createSignal, createEffect, onMount, onCleanup } from 'solid-js'
|
import { Show, createSignal, createEffect, onMount, onCleanup } from 'solid-js'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { Modal } from './Modal'
|
import { Modal } from './Modal'
|
||||||
import { AuthModal } from './AuthModal'
|
import { AuthModal } from './AuthModal'
|
||||||
import { useModalStore } from '../../stores/ui'
|
import { useModalStore } from '../../stores/ui'
|
||||||
import { router, ROUTES, useRouter } from '../../stores/router'
|
import { router, useRouter } from '../../stores/router'
|
||||||
import styles from './Header.module.scss'
|
import styles from './Header.module.scss'
|
||||||
import { getPagePath } from '@nanostores/router'
|
import { getPagePath } from '@nanostores/router'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Accessor, createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
|
import { Accessor, createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { Title } from '@solidjs/meta'
|
import { Title } from '@solidjs/meta'
|
||||||
|
@ -17,7 +17,6 @@ import { imageProxy } from '../../utils/imageProxy'
|
||||||
import { GrowingTextarea } from '../_shared/GrowingTextarea'
|
import { GrowingTextarea } from '../_shared/GrowingTextarea'
|
||||||
import { VideoUploader } from '../Editor/VideoUploader'
|
import { VideoUploader } from '../Editor/VideoUploader'
|
||||||
import { AudioUploader } from '../Editor/AudioUploader'
|
import { AudioUploader } from '../Editor/AudioUploader'
|
||||||
import { VideoPlayer } from '../_shared/VideoPlayer'
|
|
||||||
import { slugify } from '../../utils/slugify'
|
import { slugify } from '../../utils/slugify'
|
||||||
import { SolidSwiper } from '../_shared/SolidSwiper'
|
import { SolidSwiper } from '../_shared/SolidSwiper'
|
||||||
import { DropArea } from '../_shared/DropArea'
|
import { DropArea } from '../_shared/DropArea'
|
||||||
|
@ -136,7 +135,7 @@ export const EditView = (props: Props) => {
|
||||||
setForm('media', JSON.stringify(data))
|
setForm('media', JSON.stringify(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImageDelete = (index) => {
|
const handleMediaDelete = (index) => {
|
||||||
const copy = [...mediaItems()]
|
const copy = [...mediaItems()]
|
||||||
copy.splice(index, 1)
|
copy.splice(index, 1)
|
||||||
setForm('media', JSON.stringify(copy))
|
setForm('media', JSON.stringify(copy))
|
||||||
|
@ -307,36 +306,18 @@ export const EditView = (props: Props) => {
|
||||||
editorMode={true}
|
editorMode={true}
|
||||||
images={mediaItems()}
|
images={mediaItems()}
|
||||||
onImageChange={handleMediaChange}
|
onImageChange={handleMediaChange}
|
||||||
onImageDelete={(index) => handleImageDelete(index)}
|
onImageDelete={(index) => handleMediaDelete(index)}
|
||||||
onImagesAdd={(value) => handleAddMedia(value)}
|
onImagesAdd={(value) => handleAddMedia(value)}
|
||||||
onImagesSorted={(value) => handleSortedMedia(value)}
|
onImagesSorted={(value) => handleSortedMedia(value)}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={props.shout.layout === 'video'}>
|
<Show when={props.shout.layout === 'video'}>
|
||||||
<Show
|
<VideoUploader
|
||||||
when={form.media}
|
video={mediaItems()}
|
||||||
fallback={
|
onVideoAdd={(data) => handleAddMedia(data)}
|
||||||
<VideoUploader
|
onVideoDelete={(index) => handleMediaDelete(index)}
|
||||||
data={(data) => {
|
/>
|
||||||
handleAddMedia(data)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<For each={mediaItems()}>
|
|
||||||
{(mediaItem) => (
|
|
||||||
<>
|
|
||||||
<VideoPlayer
|
|
||||||
videoUrl={mediaItem?.url}
|
|
||||||
title={mediaItem?.title}
|
|
||||||
description={mediaItem?.body}
|
|
||||||
deleteAction={() => setForm('media', null)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={props.shout.layout === 'audio'}>
|
<Show when={props.shout.layout === 'audio'}>
|
||||||
|
|
|
@ -25,7 +25,6 @@ interface Props {
|
||||||
export const Slider = (props: Props) => {
|
export const Slider = (props: Props) => {
|
||||||
let el: HTMLDivElement | undefined
|
let el: HTMLDivElement | undefined
|
||||||
let thumbsEl: HTMLDivElement | undefined
|
let thumbsEl: HTMLDivElement | undefined
|
||||||
let pagEl: HTMLDivElement | undefined
|
|
||||||
let nextEl: HTMLDivElement | undefined
|
let nextEl: HTMLDivElement | undefined
|
||||||
let prevEl: HTMLDivElement | undefined
|
let prevEl: HTMLDivElement | undefined
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 1rem 0;
|
margin: 1rem auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@include media-breakpoint-up(md) {
|
&.articleView {
|
||||||
margin: 1rem -16.6666% 2rem -25%;
|
@include media-breakpoint-up(md) {
|
||||||
|
margin: 1rem -16.6666% 2rem -25%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
|
|
|
@ -11,7 +11,8 @@ type Props = {
|
||||||
title?: string
|
title?: string
|
||||||
description?: string
|
description?: string
|
||||||
class?: string
|
class?: string
|
||||||
deleteAction?: () => void
|
onVideoDelete?: () => void
|
||||||
|
articleView?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VideoPlayer = (props: Props) => {
|
export const VideoPlayer = (props: Props) => {
|
||||||
|
@ -37,15 +38,15 @@ export const VideoPlayer = (props: Props) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={clsx(styles.VideoPlayer, props.class)}>
|
<div class={clsx(styles.VideoPlayer, props.class, { [styles.articleView]: props.articleView })}>
|
||||||
<Show when={props.deleteAction}>
|
<Show when={props.onVideoDelete}>
|
||||||
<Popover content={t('Delete')}>
|
<Popover content={t('Delete')}>
|
||||||
{(triggerRef: (el) => void) => (
|
{(triggerRef: (el) => void) => (
|
||||||
<Button
|
<Button
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
size="S"
|
size="S"
|
||||||
class={styles.deleteAction}
|
class={styles.deleteAction}
|
||||||
onClick={props.deleteAction}
|
onClick={() => props.onVideoDelete()}
|
||||||
value={<Icon class={styles.deleteIcon} name="delete" />}
|
value={<Icon class={styles.deleteIcon} name="delete" />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -45,5 +45,5 @@ export type MediaItem = {
|
||||||
|
|
||||||
export type UploadedFile = {
|
export type UploadedFile = {
|
||||||
url: string
|
url: string
|
||||||
originalFilename: string
|
originalFilename?: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const composeMediaItems = (value: UploadedFile[], optionalParams = {}) =>
|
||||||
return {
|
return {
|
||||||
url: fileData.url,
|
url: fileData.url,
|
||||||
source: '',
|
source: '',
|
||||||
title: removeFileExtension(fileData.originalFilename),
|
title: fileData.originalFilename ? removeFileExtension(fileData.originalFilename) : '',
|
||||||
body: '',
|
body: '',
|
||||||
...optionalParams
|
...optionalParams
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user