mirror of
https://github.com/Qortal/q-mail.git
synced 2025-02-11 17:55:56 +00:00
remove files
This commit is contained in:
parent
ddedcc5c75
commit
8a6f803c12
7
src/assets/svgs/IconTypes.ts
Normal file
7
src/assets/svgs/IconTypes.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface IconTypes {
|
||||
color?: string;
|
||||
height: string;
|
||||
width: string;
|
||||
className?: string;
|
||||
onClickFunc?: (e?: any) => void;
|
||||
}
|
@ -1,218 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import { styled, useTheme } from '@mui/material/styles'
|
||||
import Box from '@mui/material/Box'
|
||||
import Typography from '@mui/material/Typography'
|
||||
|
||||
import AudiotrackIcon from '@mui/icons-material/Audiotrack'
|
||||
import { MyContext } from '../wrappers/DownloadWrapper'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { RootState } from '../state/store'
|
||||
import { CircularProgress } from '@mui/material'
|
||||
import {
|
||||
setCurrAudio,
|
||||
setShowingAudioPlayer
|
||||
} from '../state/features/globalSlice'
|
||||
|
||||
const Widget = styled('div')(({ theme }) => ({
|
||||
padding: 16,
|
||||
borderRadius: 16,
|
||||
maxWidth: '100%',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
// backgroundColor:
|
||||
// theme.palette.mode === 'dark' ? 'rgba(0,0,0,0.6)' : 'rgba(255,255,255,0.4)',
|
||||
backdropFilter: 'blur(40px)',
|
||||
background: 'skyblue',
|
||||
transition: '0.2s all',
|
||||
'&:hover': {
|
||||
opacity: 0.75
|
||||
}
|
||||
}))
|
||||
|
||||
const CoverImage = styled('div')({
|
||||
width: 100,
|
||||
height: 100,
|
||||
objectFit: 'cover',
|
||||
overflow: 'hidden',
|
||||
flexShrink: 0,
|
||||
borderRadius: 8,
|
||||
backgroundColor: 'rgba(0,0,0,0.08)',
|
||||
'& > img': {
|
||||
width: '100%'
|
||||
}
|
||||
})
|
||||
|
||||
const TinyText = styled(Typography)({
|
||||
fontSize: '0.75rem',
|
||||
opacity: 0.38,
|
||||
fontWeight: 500,
|
||||
letterSpacing: 0.2
|
||||
})
|
||||
|
||||
interface IAudioElement {
|
||||
onClick: () => void
|
||||
title: string
|
||||
description: string
|
||||
author: string
|
||||
audioInfo?: any
|
||||
postId?: string
|
||||
user?: string
|
||||
}
|
||||
|
||||
export default function AudioElement({
|
||||
onClick,
|
||||
title,
|
||||
description,
|
||||
author,
|
||||
audioInfo,
|
||||
postId,
|
||||
user
|
||||
}: IAudioElement) {
|
||||
const { downloadVideo } = React.useContext(MyContext)
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false)
|
||||
const { downloads } = useSelector((state: RootState) => state.global)
|
||||
const dispatch = useDispatch()
|
||||
const download = React.useMemo(() => {
|
||||
if (!downloads || !audioInfo?.identifier) return {}
|
||||
const findDownload = downloads[audioInfo?.identifier]
|
||||
|
||||
if (!findDownload) return {}
|
||||
return findDownload
|
||||
}, [downloads, audioInfo])
|
||||
|
||||
const resourceStatus = React.useMemo(() => {
|
||||
return download?.status || {}
|
||||
}, [download])
|
||||
const handlePlay = () => {
|
||||
if (!postId) return
|
||||
const { name, service, identifier } = audioInfo
|
||||
|
||||
if (download && resourceStatus?.status === 'READY') {
|
||||
dispatch(setShowingAudioPlayer(true))
|
||||
dispatch(setCurrAudio(identifier))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
downloadVideo({
|
||||
name,
|
||||
service,
|
||||
identifier,
|
||||
blogPost: {
|
||||
postId,
|
||||
user,
|
||||
audioTitle: title,
|
||||
audioDescription: description,
|
||||
audioAuthor: author
|
||||
}
|
||||
})
|
||||
dispatch(setCurrAudio(identifier))
|
||||
dispatch(setShowingAudioPlayer(true))
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (resourceStatus?.status === 'READY') {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}, [resourceStatus])
|
||||
return (
|
||||
<Box
|
||||
onClick={handlePlay}
|
||||
sx={{
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<Widget>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<CoverImage>
|
||||
<AudiotrackIcon
|
||||
sx={{
|
||||
width: '90%',
|
||||
height: 'auto'
|
||||
}}
|
||||
/>
|
||||
</CoverImage>
|
||||
<Box sx={{ ml: 1.5, minWidth: 0 }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
fontWeight={500}
|
||||
>
|
||||
{author}
|
||||
</Typography>
|
||||
<Typography noWrap>
|
||||
<b>{title}</b>
|
||||
</Typography>
|
||||
<Typography noWrap letterSpacing={-0.25}>
|
||||
{description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
{((resourceStatus.status && resourceStatus?.status !== 'READY') ||
|
||||
isLoading) && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
zIndex={4999}
|
||||
bgcolor="rgba(0, 0, 0, 0.6)"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px',
|
||||
padding: '16px',
|
||||
borderRadius: '16px'
|
||||
}}
|
||||
>
|
||||
<CircularProgress color="secondary" />
|
||||
{resourceStatus && (
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
component="div"
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontSize: '14px'
|
||||
}}
|
||||
>
|
||||
{resourceStatus?.status === 'REFETCHING' ? (
|
||||
<>
|
||||
<>
|
||||
{(
|
||||
(resourceStatus?.localChunkCount /
|
||||
resourceStatus?.totalChunkCount) *
|
||||
100
|
||||
)?.toFixed(0)}
|
||||
%
|
||||
</>
|
||||
|
||||
<> Refetching in 2 minutes</>
|
||||
</>
|
||||
) : resourceStatus?.status === 'DOWNLOADED' ? (
|
||||
<>Download Completed: building audio...</>
|
||||
) : resourceStatus?.status !== 'READY' ? (
|
||||
<>
|
||||
{(
|
||||
(resourceStatus?.localChunkCount /
|
||||
resourceStatus?.totalChunkCount) *
|
||||
100
|
||||
)?.toFixed(0)}
|
||||
%
|
||||
</>
|
||||
) : (
|
||||
<>Download Completed: fetching audio...</>
|
||||
)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Widget>
|
||||
</Box>
|
||||
)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export function convertQortalLinks(inputHtml) {
|
||||
export function convertQortalLinks(inputHtml:string) {
|
||||
// Regular expression to match 'qortal://...' URLs.
|
||||
// This will stop at the first whitespace, comma, or HTML tag
|
||||
var regex = /(qortal:\/\/[^\s,<]+)/g;
|
||||
|
@ -1,492 +0,0 @@
|
||||
import React, { useContext, useMemo, useRef, useState } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Box, IconButton, Slider } from '@mui/material'
|
||||
import { CircularProgress, Typography } from '@mui/material'
|
||||
|
||||
import {
|
||||
PlayArrow,
|
||||
Pause,
|
||||
VolumeUp,
|
||||
Fullscreen,
|
||||
PictureInPicture
|
||||
} from '@mui/icons-material'
|
||||
import { styled } from '@mui/system'
|
||||
import { MyContext } from '../../wrappers/DownloadWrapper'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { RootState } from '../../state/store'
|
||||
|
||||
const VideoContainer = styled(Box)`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
`
|
||||
|
||||
const VideoElement = styled('video')`
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: rgb(33, 33, 33);
|
||||
`
|
||||
|
||||
const ControlsContainer = styled(Box)`
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 8px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
`
|
||||
|
||||
interface VideoPlayerProps {
|
||||
src?: string
|
||||
poster?: string
|
||||
name?: string
|
||||
identifier?: string
|
||||
service?: string
|
||||
autoplay?: boolean
|
||||
from?: string | null
|
||||
setCount?: () => void
|
||||
customStyle?: any
|
||||
user?: string
|
||||
postId?: string
|
||||
}
|
||||
|
||||
export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
poster,
|
||||
name,
|
||||
identifier,
|
||||
service,
|
||||
autoplay = true,
|
||||
from = null,
|
||||
setCount,
|
||||
customStyle = {},
|
||||
user = '',
|
||||
postId = ''
|
||||
}) => {
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null)
|
||||
const [playing, setPlaying] = useState(false)
|
||||
const [volume, setVolume] = useState(1)
|
||||
const [progress, setProgress] = useState(0)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [startPlay, setStartPlay] = useState(false)
|
||||
|
||||
const { downloads } = useSelector((state: RootState) => state.global)
|
||||
|
||||
const download = useMemo(() => {
|
||||
if (!downloads || !identifier) return {}
|
||||
const findDownload = downloads[identifier]
|
||||
|
||||
if (!findDownload) return {}
|
||||
return findDownload
|
||||
}, [downloads, identifier])
|
||||
|
||||
const src = useMemo(() => {
|
||||
return download?.url || ''
|
||||
}, [download?.url])
|
||||
const resourceStatus = useMemo(() => {
|
||||
return download?.status || {}
|
||||
}, [download])
|
||||
|
||||
const toggleRef = useRef<any>(null)
|
||||
const { downloadVideo } = useContext(MyContext)
|
||||
const togglePlay = async () => {
|
||||
if (!videoRef.current) return
|
||||
setStartPlay(true)
|
||||
if (!src) {
|
||||
const el = document.getElementById('videoWrapper')
|
||||
if (el) {
|
||||
el?.parentElement?.removeChild(el)
|
||||
}
|
||||
ReactDOM.flushSync(() => {
|
||||
setIsLoading(true)
|
||||
})
|
||||
getSrc()
|
||||
}
|
||||
if (playing) {
|
||||
videoRef.current.pause()
|
||||
} else {
|
||||
videoRef.current.play()
|
||||
}
|
||||
setPlaying(!playing)
|
||||
}
|
||||
|
||||
const onVolumeChange = (_: any, value: number | number[]) => {
|
||||
if (!videoRef.current) return
|
||||
videoRef.current.volume = value as number
|
||||
setVolume(value as number)
|
||||
}
|
||||
|
||||
const onProgressChange = (_: any, value: number | number[]) => {
|
||||
if (!videoRef.current) return
|
||||
videoRef.current.currentTime = value as number
|
||||
setProgress(value as number)
|
||||
if (!playing) {
|
||||
videoRef.current.play()
|
||||
setPlaying(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleEnded = () => {
|
||||
setPlaying(false)
|
||||
}
|
||||
|
||||
const updateProgress = () => {
|
||||
if (!videoRef.current) return
|
||||
setProgress(videoRef.current.currentTime)
|
||||
}
|
||||
|
||||
const [isFullscreen, setIsFullscreen] = useState(false)
|
||||
|
||||
const enterFullscreen = () => {
|
||||
if (!videoRef.current) return
|
||||
if (videoRef.current.requestFullscreen) {
|
||||
videoRef.current.requestFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
const exitFullscreen = () => {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
if (!isFullscreen) {
|
||||
enterFullscreen()
|
||||
} else {
|
||||
exitFullscreen()
|
||||
}
|
||||
}
|
||||
const togglePictureInPicture = async () => {
|
||||
if (!videoRef.current) return
|
||||
if (document.pictureInPictureElement === videoRef.current) {
|
||||
await document.exitPictureInPicture()
|
||||
} else {
|
||||
await videoRef.current.requestPictureInPicture()
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleFullscreenChange = () => {
|
||||
setIsFullscreen(!!document.fullscreenElement)
|
||||
}
|
||||
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
||||
return () => {
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleLoadedMetadata = () => {
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
const handleCanPlay = () => {
|
||||
if (setCount) {
|
||||
setCount()
|
||||
}
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
const getSrc = React.useCallback(async () => {
|
||||
if (!name || !identifier || !service || !postId || !user) return
|
||||
try {
|
||||
downloadVideo({
|
||||
name,
|
||||
service,
|
||||
identifier,
|
||||
blogPost: {
|
||||
postId,
|
||||
user
|
||||
}
|
||||
})
|
||||
} catch (error) {}
|
||||
}, [identifier, name, service])
|
||||
|
||||
React.useEffect(() => {
|
||||
const videoElement = videoRef.current
|
||||
|
||||
const handleLeavePictureInPicture = async (event: any) => {
|
||||
const target = event?.target
|
||||
if (target) {
|
||||
target.pause()
|
||||
if (setPlaying) {
|
||||
setPlaying(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (videoElement) {
|
||||
videoElement.addEventListener(
|
||||
'leavepictureinpicture',
|
||||
handleLeavePictureInPicture
|
||||
)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (videoElement) {
|
||||
videoElement.removeEventListener(
|
||||
'leavepictureinpicture',
|
||||
handleLeavePictureInPicture
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
const videoElement = videoRef.current
|
||||
|
||||
const minimizeVideo = async () => {
|
||||
if (!videoElement) return
|
||||
const handleClose = () => {
|
||||
if (videoElement && videoElement.parentElement) {
|
||||
const el = document.getElementById('videoWrapper')
|
||||
if (el) {
|
||||
el?.parentElement?.removeChild(el)
|
||||
}
|
||||
}
|
||||
}
|
||||
const createCloseButton = (): HTMLButtonElement => {
|
||||
const closeButton = document.createElement('button')
|
||||
closeButton.textContent = 'X'
|
||||
closeButton.style.position = 'absolute'
|
||||
closeButton.style.top = '0'
|
||||
closeButton.style.right = '0'
|
||||
closeButton.style.backgroundColor = 'rgba(255, 255, 255, 0.7)'
|
||||
closeButton.style.border = 'none'
|
||||
closeButton.style.fontWeight = 'bold'
|
||||
closeButton.style.fontSize = '1.2rem'
|
||||
closeButton.style.cursor = 'pointer'
|
||||
closeButton.style.padding = '2px 8px'
|
||||
closeButton.style.borderRadius = '0 0 0 4px'
|
||||
|
||||
closeButton.addEventListener('click', handleClose)
|
||||
|
||||
return closeButton
|
||||
}
|
||||
const buttonClose = createCloseButton()
|
||||
const videoWrapper = document.createElement('div')
|
||||
videoWrapper.id = 'videoWrapper'
|
||||
videoWrapper.style.position = 'fixed'
|
||||
videoWrapper.style.zIndex = '900000009'
|
||||
videoWrapper.style.bottom = '0px'
|
||||
videoWrapper.style.right = '0px'
|
||||
|
||||
videoElement.parentElement?.insertBefore(videoWrapper, videoElement)
|
||||
videoWrapper.appendChild(videoElement)
|
||||
|
||||
videoWrapper.appendChild(buttonClose)
|
||||
videoElement.controls = true
|
||||
videoElement.style.height = 'auto'
|
||||
videoElement.style.width = '300px'
|
||||
|
||||
document.body.appendChild(videoWrapper)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (videoElement) {
|
||||
if (videoElement && !videoElement.paused && !videoElement.ended) {
|
||||
minimizeVideo()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
function formatTime(seconds: number): string {
|
||||
seconds = Math.floor(seconds)
|
||||
|
||||
let minutes: number | string = Math.floor(seconds / 60)
|
||||
let remainingSeconds: number | string = seconds % 60
|
||||
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes
|
||||
}
|
||||
if (remainingSeconds < 10) {
|
||||
remainingSeconds = '0' + remainingSeconds
|
||||
}
|
||||
|
||||
return minutes + ':' + remainingSeconds
|
||||
}
|
||||
|
||||
return (
|
||||
<VideoContainer
|
||||
style={{
|
||||
padding: from === 'create' ? '8px' : 0
|
||||
}}
|
||||
>
|
||||
{isLoading && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
zIndex={4999}
|
||||
bgcolor="rgba(0, 0, 0, 0.6)"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px'
|
||||
}}
|
||||
>
|
||||
<CircularProgress color="secondary" />
|
||||
{resourceStatus && (
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
component="div"
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontSize: '18px'
|
||||
}}
|
||||
>
|
||||
{resourceStatus?.status === 'REFETCHING' ? (
|
||||
<>
|
||||
<>
|
||||
{(
|
||||
(resourceStatus?.localChunkCount /
|
||||
resourceStatus?.totalChunkCount) *
|
||||
100
|
||||
)?.toFixed(0)}
|
||||
%
|
||||
</>
|
||||
|
||||
<> Refetching in 2 minutes</>
|
||||
</>
|
||||
) : resourceStatus?.status === 'DOWNLOADED' ? (
|
||||
<>Download Completed: building video...</>
|
||||
) : resourceStatus?.status !== 'READY' ? (
|
||||
<>
|
||||
{(
|
||||
(resourceStatus?.localChunkCount /
|
||||
resourceStatus?.totalChunkCount) *
|
||||
100
|
||||
)?.toFixed(0)}
|
||||
%
|
||||
</>
|
||||
) : (
|
||||
<>Download Completed: fetching video...</>
|
||||
)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{((!src && !isLoading) || !startPlay) && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
zIndex={500}
|
||||
bgcolor="rgba(0, 0, 0, 0.6)"
|
||||
onClick={() => {
|
||||
if (from === 'create') return
|
||||
|
||||
togglePlay()
|
||||
}}
|
||||
sx={{
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<PlayArrow
|
||||
sx={{
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
color: 'white'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<VideoElement
|
||||
ref={videoRef}
|
||||
src={!startPlay ? '' : src}
|
||||
poster={poster}
|
||||
onTimeUpdate={updateProgress}
|
||||
autoPlay={autoplay}
|
||||
onEnded={handleEnded}
|
||||
// onLoadedMetadata={handleLoadedMetadata}
|
||||
onCanPlay={handleCanPlay}
|
||||
preload="metadata"
|
||||
style={{
|
||||
...customStyle
|
||||
}}
|
||||
/>
|
||||
<ControlsContainer
|
||||
style={{
|
||||
bottom: from === 'create' ? '15px' : 0
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)'
|
||||
}}
|
||||
onClick={togglePlay}
|
||||
>
|
||||
{playing ? <Pause /> : <PlayArrow />}
|
||||
</IconButton>
|
||||
<Slider
|
||||
value={progress}
|
||||
onChange={onProgressChange}
|
||||
min={0}
|
||||
max={videoRef.current?.duration || 100}
|
||||
sx={{ flexGrow: 1, mx: 2 }}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '14px',
|
||||
marginRight: '5px',
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
visibility:
|
||||
!videoRef.current?.duration || !progress ? 'hidden' : 'visible'
|
||||
}}
|
||||
>
|
||||
{progress && videoRef.current?.duration && formatTime(progress)}/
|
||||
{progress &&
|
||||
videoRef.current?.duration &&
|
||||
formatTime(videoRef.current?.duration)}
|
||||
</Typography>
|
||||
<VolumeUp />
|
||||
<Slider
|
||||
value={volume}
|
||||
onChange={onVolumeChange}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
/>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
marginLeft: '15px'
|
||||
}}
|
||||
ref={toggleRef}
|
||||
onClick={togglePictureInPicture}
|
||||
>
|
||||
<PictureInPicture />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)'
|
||||
}}
|
||||
onClick={toggleFullscreen}
|
||||
>
|
||||
<Fullscreen />
|
||||
</IconButton>
|
||||
</ControlsContainer>
|
||||
</VideoContainer>
|
||||
)
|
||||
}
|
@ -1,855 +0,0 @@
|
||||
import React, { useMemo, useRef, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import {
|
||||
Button,
|
||||
Box,
|
||||
Typography,
|
||||
CardHeader,
|
||||
Avatar,
|
||||
useTheme
|
||||
} from '@mui/material'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { styled } from '@mui/system'
|
||||
import AudiotrackIcon from '@mui/icons-material/Audiotrack'
|
||||
import ReadOnlySlate from '../../components/editor/ReadOnlySlate'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { RootState } from '../../state/store'
|
||||
import { checkStructure } from '../../utils/checkStructure'
|
||||
import { BlogContent } from '../../interfaces/interfaces'
|
||||
import {
|
||||
setAudio,
|
||||
setCurrAudio,
|
||||
setIsLoadingGlobal,
|
||||
setVisitingBlog
|
||||
} from '../../state/features/globalSlice'
|
||||
import { VideoPlayer } from '../../components/common/VideoPlayer'
|
||||
import { AudioPlayer, IPlaylist } from '../../components/common/AudioPlayer'
|
||||
import { Responsive, WidthProvider } from 'react-grid-layout'
|
||||
import '/node_modules/react-grid-layout/css/styles.css'
|
||||
import '/node_modules/react-resizable/css/styles.css'
|
||||
import DynamicHeightItem from '../../components/DynamicHeightItem'
|
||||
import {
|
||||
addPrefix,
|
||||
buildIdentifierFromCreateTitleIdAndId
|
||||
} from '../../utils/blogIdformats'
|
||||
import { DynamicHeightItemMinimal } from '../../components/DynamicHeightItemMinimal'
|
||||
import { ReusableModal } from '../../components/modals/ReusableModal'
|
||||
import AudioElement from '../../components/AudioElement'
|
||||
import ErrorBoundary from '../../components/common/ErrorBoundary'
|
||||
import { CommentSection } from '../../components/common/Comments/CommentSection'
|
||||
import { Tipping } from '../../components/common/Tipping/Tipping'
|
||||
import FileElement from '../../components/FileElement'
|
||||
const ResponsiveGridLayout = WidthProvider(Responsive)
|
||||
const initialMinHeight = 2 // Define an initial minimum height for grid items
|
||||
|
||||
const md = [
|
||||
{ i: 'a', x: 0, y: 0, w: 4, h: initialMinHeight },
|
||||
{ i: 'b', x: 6, y: 0, w: 4, h: initialMinHeight }
|
||||
]
|
||||
const sm = [
|
||||
{ i: 'a', x: 0, y: 0, w: 6, h: initialMinHeight },
|
||||
{ i: 'b', x: 6, y: 0, w: 6, h: initialMinHeight }
|
||||
]
|
||||
const xs = [
|
||||
{ i: 'a', x: 0, y: 0, w: 6, h: initialMinHeight },
|
||||
{ i: 'b', x: 6, y: 0, w: 6, h: initialMinHeight }
|
||||
]
|
||||
|
||||
interface ILayoutGeneralSettings {
|
||||
padding: number
|
||||
blogPostType: string
|
||||
}
|
||||
export const BlogIndividualPost = () => {
|
||||
const { user, postId, blog } = useParams()
|
||||
const blogFull = React.useMemo(() => {
|
||||
if (!blog) return ''
|
||||
return addPrefix(blog)
|
||||
}, [blog])
|
||||
const { user: userState } = useSelector((state: RootState) => state.auth)
|
||||
const { audios, audioPostId } = useSelector(
|
||||
(state: RootState) => state.global
|
||||
)
|
||||
|
||||
const [avatarUrl, setAvatarUrl] = React.useState<string>('')
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
const theme = useTheme()
|
||||
// const [currAudio, setCurrAudio] = React.useState<number | null>(null)
|
||||
const [layouts, setLayouts] = React.useState<any>({ md, sm, xs })
|
||||
const [count, setCount] = React.useState<number>(1)
|
||||
const [layoutGeneralSettings, setLayoutGeneralSettings] =
|
||||
React.useState<ILayoutGeneralSettings | null>(null)
|
||||
const [currentBreakpoint, setCurrentBreakpoint] = React.useState<any>()
|
||||
const handleLayoutChange = (layout: any, layoutss: any) => {
|
||||
// const redoLayouts = setAutoHeight(layoutss)
|
||||
setLayouts(layoutss)
|
||||
// saveLayoutsToLocalStorage(layoutss)
|
||||
}
|
||||
const [blogContent, setBlogContent] = React.useState<BlogContent | null>(null)
|
||||
const [isOpenSwitchPlaylistModal, setisOpenSwitchPlaylistModal] =
|
||||
useState<boolean>(false)
|
||||
const tempSaveAudio = useRef<any>(null)
|
||||
const saveAudio = React.useRef<any>(null)
|
||||
|
||||
const fullPostId = useMemo(() => {
|
||||
if (!blog || !postId) return ''
|
||||
dispatch(setIsLoadingGlobal(true))
|
||||
const formBlogId = addPrefix(blog)
|
||||
const formPostId = buildIdentifierFromCreateTitleIdAndId(formBlogId, postId)
|
||||
return formPostId
|
||||
}, [blog, postId])
|
||||
const getBlogPost = React.useCallback(async () => {
|
||||
try {
|
||||
if (!blog || !postId) return
|
||||
dispatch(setIsLoadingGlobal(true))
|
||||
const formBlogId = addPrefix(blog)
|
||||
const formPostId = buildIdentifierFromCreateTitleIdAndId(
|
||||
formBlogId,
|
||||
postId
|
||||
)
|
||||
const url = `/arbitrary/BLOG_POST/${user}/${formPostId}`
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const responseData = await response.json()
|
||||
|
||||
if (checkStructure(responseData)) {
|
||||
setBlogContent(responseData)
|
||||
if (responseData?.layouts) {
|
||||
setLayouts(responseData?.layouts)
|
||||
}
|
||||
if (responseData?.layoutGeneralSettings) {
|
||||
setLayoutGeneralSettings(responseData.layoutGeneralSettings)
|
||||
}
|
||||
const filteredAudios = (responseData?.postContent || []).filter(
|
||||
(content: any) => content?.type === 'audio'
|
||||
)
|
||||
|
||||
const transformAudios = filteredAudios?.map((fa: any) => {
|
||||
return {
|
||||
...(fa?.content || {}),
|
||||
id: fa?.id
|
||||
}
|
||||
})
|
||||
|
||||
if (!audios && transformAudios.length > 0) {
|
||||
saveAudio.current = { audios: transformAudios, postId: formPostId }
|
||||
dispatch(setAudio({ audios: transformAudios, postId: formPostId }))
|
||||
} else if (
|
||||
formPostId === audioPostId &&
|
||||
audios?.length !== transformAudios.length
|
||||
) {
|
||||
tempSaveAudio.current = {
|
||||
message:
|
||||
"This post's audio playlist has updated. Would you like to switch?"
|
||||
}
|
||||
setisOpenSwitchPlaylistModal(true)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
dispatch(setIsLoadingGlobal(false))
|
||||
}
|
||||
}, [user, postId, blog])
|
||||
React.useEffect(() => {
|
||||
getBlogPost()
|
||||
}, [postId])
|
||||
|
||||
const switchPlayList = () => {
|
||||
const filteredAudios = (blogContent?.postContent || []).filter(
|
||||
(content) => content?.type === 'audio'
|
||||
)
|
||||
|
||||
const formatAudios = filteredAudios.map((fa) => {
|
||||
return {
|
||||
...(fa?.content || {}),
|
||||
id: fa?.id
|
||||
}
|
||||
})
|
||||
if (!blog || !postId) return
|
||||
const formBlogId = addPrefix(blog)
|
||||
const formPostId = buildIdentifierFromCreateTitleIdAndId(formBlogId, postId)
|
||||
dispatch(setAudio({ audios: formatAudios, postId: formPostId }))
|
||||
if (tempSaveAudio?.current?.currentSelection) {
|
||||
const findIndex = (formatAudios || []).findIndex(
|
||||
(item) =>
|
||||
item?.identifier ===
|
||||
tempSaveAudio?.current?.currentSelection?.content?.identifier
|
||||
)
|
||||
if (findIndex >= 0) {
|
||||
dispatch(setCurrAudio(findIndex))
|
||||
}
|
||||
}
|
||||
setisOpenSwitchPlaylistModal(false)
|
||||
}
|
||||
|
||||
const getAvatar = React.useCallback(async () => {
|
||||
try {
|
||||
let url = await qortalRequest({
|
||||
action: 'GET_QDN_RESOURCE_URL',
|
||||
name: user,
|
||||
service: 'THUMBNAIL',
|
||||
identifier: 'qortal_avatar'
|
||||
})
|
||||
|
||||
setAvatarUrl(url)
|
||||
} catch (error) {}
|
||||
}, [user])
|
||||
React.useEffect(() => {
|
||||
getAvatar()
|
||||
}, [])
|
||||
|
||||
const onBreakpointChange = React.useCallback((newBreakpoint: any) => {
|
||||
setCurrentBreakpoint(newBreakpoint)
|
||||
}, [])
|
||||
|
||||
const onResizeStop = React.useCallback((layout: any, layoutItem: any) => {
|
||||
// Update the layout state with the new position and size of the component
|
||||
setCount((prev) => prev + 1)
|
||||
}, [])
|
||||
|
||||
// const audios = React.useMemo<IPlaylist[]>(() => {
|
||||
// const filteredAudios = (blogContent?.postContent || []).filter(
|
||||
// (content) => content.type === 'audio'
|
||||
// )
|
||||
|
||||
// return filteredAudios.map((fa) => {
|
||||
// return {
|
||||
// ...fa.content,
|
||||
// id: fa.id
|
||||
// }
|
||||
// })
|
||||
// }, [blogContent])
|
||||
|
||||
const handleResize = () => {
|
||||
setCount((prev) => prev + 1)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener('resize', handleResize)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleCount = React.useCallback(() => {
|
||||
// Update the layout state with the new position and size of the component
|
||||
setCount((prev) => prev + 1)
|
||||
}, [])
|
||||
|
||||
const getBlog = React.useCallback(async () => {
|
||||
let name = user
|
||||
if (!name) return
|
||||
if (!blogFull) return
|
||||
try {
|
||||
const urlBlog = `/arbitrary/BLOG/${name}/${blogFull}`
|
||||
const response = await fetch(urlBlog, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
const responseData = await response.json()
|
||||
dispatch(setVisitingBlog({ ...responseData, name }))
|
||||
} catch (error) {}
|
||||
}, [user, blogFull])
|
||||
|
||||
React.useEffect(() => {
|
||||
getBlog()
|
||||
}, [user, blogFull])
|
||||
|
||||
if (!blogContent) return null
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: '1400px',
|
||||
// margin: '15px',
|
||||
width: '95%',
|
||||
paddingBottom: '50px'
|
||||
}}
|
||||
>
|
||||
{user === userState?.name && (
|
||||
<Button
|
||||
sx={{ backgroundColor: theme.palette.secondary.main }}
|
||||
onClick={() => {
|
||||
navigate(`/${user}/${blog}/${postId}/edit`)
|
||||
}}
|
||||
>
|
||||
Edit Post
|
||||
</Button>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
<CardHeader
|
||||
onClick={() => {
|
||||
navigate(`/${user}/${blog}`)
|
||||
}}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
'& .MuiCardHeader-content': {
|
||||
overflow: 'hidden'
|
||||
},
|
||||
padding: '10px 0px'
|
||||
}}
|
||||
avatar={<Avatar src={avatarUrl} alt={`${user}'s avatar`} />}
|
||||
subheader={
|
||||
<Typography
|
||||
sx={{ fontFamily: 'Cairo', fontSize: '25px' }}
|
||||
color={theme.palette.text.primary}
|
||||
>{` ${user}`}</Typography>
|
||||
}
|
||||
/>
|
||||
{user && (
|
||||
<Tipping
|
||||
name={user || ''}
|
||||
onSubmit={() => {
|
||||
// setNameTip('')
|
||||
}}
|
||||
onClose={() => {
|
||||
// setNameTip('')
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h1"
|
||||
color="textPrimary"
|
||||
sx={{
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{blogContent?.title}
|
||||
</Typography>
|
||||
<CommentSection postId={fullPostId} />
|
||||
</Box>
|
||||
|
||||
{(layoutGeneralSettings?.blogPostType === 'builder' ||
|
||||
!layoutGeneralSettings?.blogPostType) && (
|
||||
<Content
|
||||
layouts={layouts}
|
||||
blogContent={blogContent}
|
||||
onResizeStop={onResizeStop}
|
||||
onBreakpointChange={onBreakpointChange}
|
||||
handleLayoutChange={handleLayoutChange}
|
||||
>
|
||||
{blogContent?.postContent?.map((section: any) => {
|
||||
if (section?.type === 'editor') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item-view">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItem
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={layoutGeneralSettings?.padding}
|
||||
>
|
||||
<ReadOnlySlate content={section.content} />
|
||||
</DynamicHeightItem>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (section?.type === 'image') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item-view">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItem
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={layoutGeneralSettings?.padding}
|
||||
>
|
||||
<img
|
||||
src={section.content.image}
|
||||
className="post-image"
|
||||
/>
|
||||
</DynamicHeightItem>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (section?.type === 'video') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item-view">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItem
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={layoutGeneralSettings?.padding}
|
||||
>
|
||||
<VideoPlayer
|
||||
name={section.content.name}
|
||||
service={section.content.service}
|
||||
identifier={section.content.identifier}
|
||||
setCount={handleCount}
|
||||
user={user}
|
||||
postId={fullPostId}
|
||||
/>
|
||||
</DynamicHeightItem>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (section?.type === 'audio') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item-view">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItem
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={layoutGeneralSettings?.padding}
|
||||
>
|
||||
<AudioElement
|
||||
key={section.id}
|
||||
audioInfo={section.content}
|
||||
postId={fullPostId}
|
||||
user={user ? user : ''}
|
||||
onClick={() => {
|
||||
if (!blog || !postId) return
|
||||
|
||||
const formBlogId = addPrefix(blog)
|
||||
const formPostId =
|
||||
buildIdentifierFromCreateTitleIdAndId(
|
||||
formBlogId,
|
||||
postId
|
||||
)
|
||||
if (audioPostId && formPostId !== audioPostId) {
|
||||
tempSaveAudio.current = {
|
||||
...(tempSaveAudio.current || {}),
|
||||
currentSelection: section,
|
||||
message:
|
||||
'You are current on a playlist. Would you like to switch?'
|
||||
}
|
||||
setisOpenSwitchPlaylistModal(true)
|
||||
} else {
|
||||
if (!audios && saveAudio?.current) {
|
||||
const findIndex = (
|
||||
saveAudio?.current?.audios || []
|
||||
).findIndex(
|
||||
(item: any) =>
|
||||
item.identifier ===
|
||||
section.content.identifier
|
||||
)
|
||||
dispatch(setAudio(saveAudio?.current))
|
||||
dispatch(setCurrAudio(findIndex))
|
||||
return
|
||||
}
|
||||
|
||||
const findIndex = (audios || []).findIndex(
|
||||
(item) =>
|
||||
item.identifier === section.content.identifier
|
||||
)
|
||||
if (findIndex >= 0) {
|
||||
dispatch(setCurrAudio(findIndex))
|
||||
}
|
||||
}
|
||||
}}
|
||||
title={section.content?.title}
|
||||
description={section.content?.description}
|
||||
author=""
|
||||
/>
|
||||
</DynamicHeightItem>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (section?.type === 'file') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItemMinimal
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={0}
|
||||
>
|
||||
<FileElement
|
||||
key={section.id}
|
||||
fileInfo={section.content}
|
||||
postId={fullPostId}
|
||||
user={user ? user : ''}
|
||||
title={section.content?.title}
|
||||
description={section.content?.description}
|
||||
mimeType={section.content?.mimeType}
|
||||
author=""
|
||||
/>
|
||||
</DynamicHeightItemMinimal>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</Content>
|
||||
)}
|
||||
{layoutGeneralSettings?.blogPostType === 'minimal' && (
|
||||
<>
|
||||
{layouts?.rows?.map((row: any, rowIndex: number) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: '25px',
|
||||
gap: 2
|
||||
}}
|
||||
>
|
||||
{row?.ids?.map((elementId: string) => {
|
||||
const section: any = blogContent?.postContent?.find(
|
||||
(el) => el?.id === elementId
|
||||
)
|
||||
if (!section) return null
|
||||
if (section?.type === 'editor') {
|
||||
return (
|
||||
<div
|
||||
key={section?.id}
|
||||
className="grid-item"
|
||||
style={{
|
||||
maxWidth: '800px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItemMinimal
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={0}
|
||||
>
|
||||
<ReadOnlySlate
|
||||
key={section.id}
|
||||
content={section.content}
|
||||
/>
|
||||
</DynamicHeightItemMinimal>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (section?.type === 'image') {
|
||||
return (
|
||||
<div key={section.id} className="grid-item">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItemMinimal
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
type="image"
|
||||
padding={0}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={section.content.image}
|
||||
className="post-image"
|
||||
style={{
|
||||
objectFit: 'contain',
|
||||
maxHeight: '50vh'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</DynamicHeightItemMinimal>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (section?.type === 'video') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItemMinimal
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={0}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
name={section.content.name}
|
||||
service={section.content.service}
|
||||
identifier={section.content.identifier}
|
||||
customStyle={{
|
||||
height: '50vh'
|
||||
}}
|
||||
user={user}
|
||||
postId={fullPostId}
|
||||
/>
|
||||
</Box>
|
||||
</DynamicHeightItemMinimal>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (section?.type === 'audio') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItemMinimal
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={0}
|
||||
>
|
||||
<AudioElement
|
||||
key={section.id}
|
||||
audioInfo={section.content}
|
||||
postId={fullPostId}
|
||||
user={user ? user : ''}
|
||||
onClick={() => {
|
||||
if (!blog || !postId) return
|
||||
const formBlogId = addPrefix(blog)
|
||||
const formPostId =
|
||||
buildIdentifierFromCreateTitleIdAndId(
|
||||
formBlogId,
|
||||
postId
|
||||
)
|
||||
if (formPostId !== audioPostId) {
|
||||
tempSaveAudio.current = {
|
||||
...(tempSaveAudio.current || {}),
|
||||
currentSelection: section,
|
||||
message:
|
||||
'You are current on a playlist. Would you like to switch?'
|
||||
}
|
||||
setisOpenSwitchPlaylistModal(true)
|
||||
} else {
|
||||
const findIndex = (audios || []).findIndex(
|
||||
(item) =>
|
||||
item.identifier ===
|
||||
section.content.identifier
|
||||
)
|
||||
if (findIndex >= 0) {
|
||||
dispatch(setCurrAudio(findIndex))
|
||||
}
|
||||
}
|
||||
}}
|
||||
title={section.content?.title}
|
||||
description={section.content?.description}
|
||||
author=""
|
||||
/>
|
||||
</DynamicHeightItemMinimal>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (section?.type === 'file') {
|
||||
return (
|
||||
<div key={section?.id} className="grid-item">
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>
|
||||
Error loading content: Invalid Data
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<DynamicHeightItemMinimal
|
||||
layouts={layouts}
|
||||
setLayouts={setLayouts}
|
||||
i={section.id}
|
||||
breakpoint={currentBreakpoint}
|
||||
count={count}
|
||||
padding={0}
|
||||
>
|
||||
<FileElement
|
||||
key={section.id}
|
||||
fileInfo={section.content}
|
||||
postId={fullPostId}
|
||||
user={user ? user : ''}
|
||||
title={section.content?.title}
|
||||
description={section.content?.description}
|
||||
mimeType={section.content?.mimeType}
|
||||
author=""
|
||||
/>
|
||||
</DynamicHeightItemMinimal>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
<ReusableModal open={isOpenSwitchPlaylistModal}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
{tempSaveAudio?.current?.message
|
||||
? tempSaveAudio?.current?.message
|
||||
: 'You are current on a playlist. Would you like to switch?'}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => setisOpenSwitchPlaylistModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="contained" onClick={switchPlayList}>
|
||||
Switch
|
||||
</Button>
|
||||
</ReusableModal>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const Content = ({
|
||||
children,
|
||||
layouts,
|
||||
blogContent,
|
||||
onResizeStop,
|
||||
onBreakpointChange,
|
||||
handleLayoutChange
|
||||
}: any) => {
|
||||
if (layouts && blogContent?.layouts) {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
fallback={
|
||||
<Typography>Error loading content: Invalid Layout</Typography>
|
||||
}
|
||||
>
|
||||
<ResponsiveGridLayout
|
||||
layouts={layouts}
|
||||
breakpoints={{ md: 996, sm: 768, xs: 480 }}
|
||||
cols={{ md: 4, sm: 3, xs: 1 }}
|
||||
measureBeforeMount={false}
|
||||
onLayoutChange={handleLayoutChange}
|
||||
autoSize={true}
|
||||
compactType={null}
|
||||
isBounded={true}
|
||||
resizeHandles={['se', 'sw', 'ne', 'nw']}
|
||||
rowHeight={25}
|
||||
onResizeStop={onResizeStop}
|
||||
onBreakpointChange={onBreakpointChange}
|
||||
isDraggable={false}
|
||||
isResizable={false}
|
||||
margin={[0, 0]}
|
||||
>
|
||||
{children}
|
||||
</ResponsiveGridLayout>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
return children
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
import { Box, Button, Typography } from '@mui/material'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { ReusableModal } from '../../components/modals/ReusableModal'
|
||||
import { CreatePostBuilder } from './CreatePostBuilder'
|
||||
import { CreatePostMinimal } from './CreatePostMinimal'
|
||||
import HandymanRoundedIcon from '@mui/icons-material/HandymanRounded'
|
||||
import HourglassFullRoundedIcon from '@mui/icons-material/HourglassFullRounded'
|
||||
import { display } from '@mui/system'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { setIsLoadingGlobal } from '../../state/features/globalSlice'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { checkStructure } from '../../utils/checkStructure'
|
||||
import { RootState } from '../../state/store'
|
||||
import {
|
||||
addPrefix,
|
||||
buildIdentifierFromCreateTitleIdAndId
|
||||
} from '../../utils/blogIdformats'
|
||||
import { Tipping } from '../../components/common/Tipping/Tipping'
|
||||
type EditorType = 'minimal' | 'builder'
|
||||
interface CreatePostProps {
|
||||
mode?: string
|
||||
}
|
||||
export const CreatePost = ({ mode }: CreatePostProps) => {
|
||||
const { user: username, postId, blog } = useParams()
|
||||
const fullPostId = useMemo(() => {
|
||||
if (!blog || !postId || mode !== 'edit') return ''
|
||||
const formBlogId = addPrefix(blog)
|
||||
const formPostId = buildIdentifierFromCreateTitleIdAndId(formBlogId, postId)
|
||||
return formPostId
|
||||
}, [blog, postId, mode])
|
||||
const { user } = useSelector((state: RootState) => state.auth)
|
||||
|
||||
const [toggleEditorType, setToggleEditorType] = useState<EditorType | null>(
|
||||
null
|
||||
)
|
||||
const [blogContentForEdit, setBlogContentForEdit] = useState<any>(null)
|
||||
const [blogMetadataForEdit, setBlogMetadataForEdit] = useState<any>(null)
|
||||
const [editType, setEditType] = useState<EditorType | null>(null)
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false)
|
||||
const dispatch = useDispatch()
|
||||
React.useEffect(() => {
|
||||
if (!toggleEditorType && mode !== 'edit') {
|
||||
setIsOpen(true)
|
||||
}
|
||||
}, [setIsOpen, toggleEditorType])
|
||||
|
||||
const switchType = () => {
|
||||
setIsOpen(true)
|
||||
}
|
||||
|
||||
const getBlogPost = React.useCallback(async () => {
|
||||
try {
|
||||
dispatch(setIsLoadingGlobal(true))
|
||||
const url = `/arbitrary/BLOG_POST/${username}/${fullPostId}`
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const responseData = await response.json()
|
||||
if (checkStructure(responseData)) {
|
||||
// setNewPostContent(responseData.postContent)
|
||||
// setTitle(responseData?.title || '')
|
||||
// setBlogInfo(responseData)
|
||||
const blogType = responseData?.layoutGeneralSettings?.blogPostType
|
||||
|
||||
if (blogType) {
|
||||
setEditType(blogType)
|
||||
setBlogContentForEdit(responseData)
|
||||
}
|
||||
//TODO - NAME SHOULD BE EXACT
|
||||
// const url2 = `/arbitrary/resources/search?mode=ALL&service=BLOG_POST&identifier=${fullPostId}&exactMatchNames=${username}&limit=1&includemetadata=true`
|
||||
const url2 = `/arbitrary/resources?service=BLOG_POST&identifier=${fullPostId}&name=${username}&limit=1&includemetadata=true`
|
||||
|
||||
const responseBlogs = await fetch(url2, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const dataMetadata = await responseBlogs.json()
|
||||
if (dataMetadata && dataMetadata.length > 0) {
|
||||
setBlogMetadataForEdit(dataMetadata[0])
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
dispatch(setIsLoadingGlobal(false))
|
||||
}
|
||||
}, [username, fullPostId])
|
||||
React.useEffect(() => {
|
||||
if (mode === 'edit') {
|
||||
getBlogPost()
|
||||
}
|
||||
}, [mode])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* {toggleEditorType === 'minimal' && (
|
||||
<Button onClick={() => switchType()}>Switch to Builder</Button>
|
||||
)}
|
||||
{toggleEditorType === 'builder' && (
|
||||
<Button onClick={() => switchType()}>Switch to Minimal</Button>
|
||||
)} */}
|
||||
{isOpen && (
|
||||
<ReusableModal
|
||||
open={isOpen}
|
||||
customStyles={{
|
||||
maxWidth: '500px'
|
||||
}}
|
||||
>
|
||||
{toggleEditorType && (
|
||||
<Typography>
|
||||
Switching editor type will delete your current progress
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 2
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
onClick={() => {
|
||||
setToggleEditorType('minimal')
|
||||
setIsOpen(false)
|
||||
}}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '20px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<Typography>Minimal Editor</Typography>
|
||||
<HourglassFullRoundedIcon />
|
||||
</Box>
|
||||
<Box
|
||||
onClick={() => {
|
||||
setToggleEditorType('builder')
|
||||
setIsOpen(false)
|
||||
}}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '20px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<Typography>Builder Editor</Typography>
|
||||
<HandymanRoundedIcon />
|
||||
</Box>
|
||||
</Box>
|
||||
<Button onClick={() => setIsOpen(false)}>Close</Button>
|
||||
</ReusableModal>
|
||||
)}
|
||||
|
||||
{toggleEditorType === 'minimal' && (
|
||||
<CreatePostMinimal switchType={switchType} />
|
||||
)}
|
||||
{toggleEditorType === 'builder' && (
|
||||
<CreatePostBuilder switchType={switchType} />
|
||||
)}
|
||||
{mode === 'edit' && editType === 'minimal' && (
|
||||
<CreatePostMinimal
|
||||
blogContentForEdit={blogContentForEdit}
|
||||
postIdForEdit={fullPostId}
|
||||
blogMetadataForEdit={blogMetadataForEdit}
|
||||
/>
|
||||
)}
|
||||
{mode === 'edit' && editType === 'builder' && (
|
||||
<CreatePostBuilder
|
||||
blogContentForEdit={blogContentForEdit}
|
||||
postIdForEdit={fullPostId}
|
||||
blogMetadataForEdit={blogMetadataForEdit}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user