mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
improve attachment render behavior
This commit is contained in:
parent
330b1b9cec
commit
15214cff3f
@ -1,4 +1,5 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
import { Skeleton } from "antd"
|
||||||
import loadable from "@loadable/component"
|
import loadable from "@loadable/component"
|
||||||
import { Carousel } from "react-responsive-carousel"
|
import { Carousel } from "react-responsive-carousel"
|
||||||
import Plyr from "plyr-react"
|
import Plyr from "plyr-react"
|
||||||
@ -28,96 +29,139 @@ import "react-responsive-carousel/lib/styles/carousel.min.css"
|
|||||||
import "plyr-react/dist/plyr.css"
|
import "plyr-react/dist/plyr.css"
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
export default class PostAttachments extends React.PureComponent {
|
const Attachment = React.memo((props) => {
|
||||||
getAttachments = (data) => {
|
const { url, id, name } = props.attachment
|
||||||
return data.map((addition, index) => {
|
|
||||||
if (typeof addition === "string") {
|
|
||||||
addition = {
|
|
||||||
url: addition,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { url, id, name } = addition
|
const [loaded, setLoaded] = React.useState(false)
|
||||||
|
|
||||||
const MediaRender = loadable(async () => {
|
const [mediaType, setMediaType] = React.useState(null)
|
||||||
let extension = null
|
const [mimeType, setMimeType] = React.useState(null)
|
||||||
|
|
||||||
try {
|
const getMediaType = async () => {
|
||||||
// get media type by parsing the url
|
let extension = null
|
||||||
const mediaTypeExt = /\.([a-zA-Z0-9]+)$/.exec(url)
|
|
||||||
|
|
||||||
if (mediaTypeExt) {
|
// get media type by parsing the url
|
||||||
extension = mediaTypeExt[1]
|
const mediaTypeExt = /\.([a-zA-Z0-9]+)$/.exec(url)
|
||||||
} else {
|
|
||||||
// try to get media by creating requesting the url
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "HEAD",
|
|
||||||
})
|
|
||||||
|
|
||||||
extension = response.headers.get("content-type").split("/")[1]
|
if (mediaTypeExt) {
|
||||||
}
|
extension = mediaTypeExt[1]
|
||||||
|
} else {
|
||||||
extension = extension.toLowerCase()
|
// try to get media by creating requesting the url
|
||||||
|
const response = await fetch(url, {
|
||||||
const mediaType = mediaTypes[extension]
|
method: "HEAD",
|
||||||
const mimeType = `${mediaType}/${extension}`
|
|
||||||
|
|
||||||
if (!mediaType) {
|
|
||||||
return () => <ContentFailed />
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (mediaType.split("/")[0]) {
|
|
||||||
case "image": {
|
|
||||||
return () => <img src={url} />
|
|
||||||
}
|
|
||||||
case "video": {
|
|
||||||
return () => <Plyr
|
|
||||||
source={{
|
|
||||||
type: "video",
|
|
||||||
sources: [{
|
|
||||||
src: url,
|
|
||||||
}],
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
controls: ["play", "progress", "current-time", "mute", "volume"],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
case "audio": {
|
|
||||||
return () => <audio controls>
|
|
||||||
<source src={url} type={mimeType} />
|
|
||||||
</audio>
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return () => <h4>
|
|
||||||
Unsupported media type [{mediaType}/{mediaTypeExt}]
|
|
||||||
</h4>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
return () => <ContentFailed />
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return <div key={index} className="addition">
|
extension = response.headers.get("content-type").split("/")[1]
|
||||||
<React.Suspense fallback={<div>Loading</div>} >
|
}
|
||||||
<MediaRender />
|
|
||||||
</React.Suspense>
|
extension = extension.toLowerCase()
|
||||||
</div>
|
|
||||||
})
|
const mediaType = mediaTypes[extension]
|
||||||
|
const mimeType = `${mediaType}/${extension}`
|
||||||
|
|
||||||
|
setMediaType(mediaType)
|
||||||
|
setMimeType(mimeType)
|
||||||
|
|
||||||
|
setLoaded(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
const renderMedia = () => {
|
||||||
return <div className="post_attachments">
|
switch (mediaType.split("/")[0]) {
|
||||||
<Carousel
|
case "image": {
|
||||||
showArrows={true}
|
return <img src={url} />
|
||||||
showStatus={false}
|
}
|
||||||
showThumbs={false}
|
case "video": {
|
||||||
showIndicators={this.props.attachments?.length > 1 ?? false}
|
return <Plyr
|
||||||
>
|
source={{
|
||||||
{this.getAttachments(this.props.attachments)}
|
type: "video",
|
||||||
</Carousel>
|
sources: [{
|
||||||
</div>
|
src: url,
|
||||||
|
}],
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
controls: ["play", "progress", "current-time", "mute", "volume"],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
case "audio": {
|
||||||
|
return <audio controls>
|
||||||
|
<source src={url} type={mimeType} />
|
||||||
|
</audio>
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return <h4>
|
||||||
|
Unsupported media type [{mediaType}/{mediaTypeExt}]
|
||||||
|
</h4>
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
getMediaType()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!loaded) {
|
||||||
|
return <Skeleton active />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loaded && !mediaType && !mimeType) {
|
||||||
|
return <ContentFailed />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="attachment" id={id}>
|
||||||
|
{renderMedia()}
|
||||||
|
</div>
|
||||||
|
})
|
||||||
|
|
||||||
|
export default (props) => {
|
||||||
|
const carouselRef = React.useRef(null)
|
||||||
|
const [attachmentIndex, setAttachmentIndex] = React.useState(0)
|
||||||
|
|
||||||
|
const handleAttachmentChange = (index) => {
|
||||||
|
const currentAttachmentIndex = carouselRef.current.state.selectedItem
|
||||||
|
const currentAttachment = carouselRef.current.itemsRef[currentAttachmentIndex].querySelector("video, audio")
|
||||||
|
|
||||||
|
if (currentAttachmentIndex !== index) {
|
||||||
|
// if the attachment is a video, pause it
|
||||||
|
if (currentAttachment) {
|
||||||
|
currentAttachment.pause()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// else if the attachment is a video, play it
|
||||||
|
if (currentAttachment) {
|
||||||
|
currentAttachment.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAttachmentIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// get attachment index from query string
|
||||||
|
const attachmentIndex = parseInt(new URLSearchParams(window.location.search).get("attachment"))
|
||||||
|
|
||||||
|
if (attachmentIndex) {
|
||||||
|
setAttachmentIndex(attachmentIndex)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return <div className="post_attachments">
|
||||||
|
<Carousel
|
||||||
|
ref={carouselRef}
|
||||||
|
showArrows={true}
|
||||||
|
showStatus={false}
|
||||||
|
showThumbs={false}
|
||||||
|
showIndicators={props.attachments?.length > 1 ?? false}
|
||||||
|
selectedItem={attachmentIndex}
|
||||||
|
onChange={handleAttachmentChange}
|
||||||
|
transitionTime={150}
|
||||||
|
stopOnHover={true}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
props.attachments.map((attachment, index) => {
|
||||||
|
return <Attachment key={index} attachment={attachment} />
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
}
|
}
|
@ -19,8 +19,13 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.carousel {
|
.carousel {
|
||||||
|
display: flex;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.slider-wrapper {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-prev,
|
.control-prev,
|
||||||
@ -82,7 +87,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addition {
|
.attachment {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
@ -5,19 +5,13 @@ import classnames from "classnames"
|
|||||||
|
|
||||||
import { processString } from "utils"
|
import { processString } from "utils"
|
||||||
|
|
||||||
import { Icons } from "components/Icons"
|
|
||||||
|
|
||||||
import PostAttachments from "../attachments"
|
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
export default React.memo((props) => {
|
export default (props) => {
|
||||||
let { message, attachments, type, data, flags } = props.data
|
let { message, data } = props.data
|
||||||
|
|
||||||
const [nsfwAccepted, setNsfwAccepted] = React.useState(false)
|
const [nsfwAccepted, setNsfwAccepted] = React.useState(false)
|
||||||
|
|
||||||
const isNSFW = flags?.includes("nsfw")
|
|
||||||
|
|
||||||
if (typeof data === "string") {
|
if (typeof data === "string") {
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(data)
|
data = JSON.parse(data)
|
||||||
@ -27,12 +21,6 @@ export default React.memo((props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClickPlaylist = () => {
|
|
||||||
if (data.playlist) {
|
|
||||||
app.AudioPlayer.startPlaylist(data.playlist)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse message
|
// parse message
|
||||||
const regexs = [
|
const regexs = [
|
||||||
{
|
{
|
||||||
@ -63,57 +51,17 @@ export default React.memo((props) => {
|
|||||||
|
|
||||||
message = processString(regexs)(message)
|
message = processString(regexs)(message)
|
||||||
|
|
||||||
const renderContent = () => {
|
|
||||||
switch (type) {
|
|
||||||
case "playlist": {
|
|
||||||
return <>
|
|
||||||
<div
|
|
||||||
className="playlistCover"
|
|
||||||
onClick={onClickPlaylist}
|
|
||||||
style={{
|
|
||||||
backgroundImage: `url(${data?.cover ?? "/assets/no_song.png"})`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="playlistTitle">
|
|
||||||
<div>
|
|
||||||
<h1>
|
|
||||||
{data.title ?? "Untitled Playlist"}
|
|
||||||
</h1>
|
|
||||||
<h3>
|
|
||||||
{data.artist}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
{message}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div className="actions">
|
|
||||||
<antd.Button onClick={onClickPlaylist}>
|
|
||||||
<Icons.PlayCircle />
|
|
||||||
Play
|
|
||||||
</antd.Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return <>
|
|
||||||
<div className="message">
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{attachments.length > 0 && <PostAttachments attachments={attachments} />}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div
|
return <div
|
||||||
className={classnames("post_content", { ["nsfw"]: isNSFW && !nsfwAccepted })}
|
className={
|
||||||
|
classnames(
|
||||||
|
"post_content",
|
||||||
|
{
|
||||||
|
["nsfw"]: props.nsfw && !nsfwAccepted
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{isNSFW && !nsfwAccepted &&
|
{props.nsfw && !nsfwAccepted &&
|
||||||
<div className="nsfw_alert">
|
<div className="nsfw_alert">
|
||||||
<h2>
|
<h2>
|
||||||
This post may contain sensitive content.
|
This post may contain sensitive content.
|
||||||
@ -125,6 +73,12 @@ export default React.memo((props) => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{renderContent()}
|
<div className="message">
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
props.children
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
})
|
}
|
@ -7,10 +7,11 @@ import { Icons } from "components/Icons"
|
|||||||
import PostHeader from "./components/header"
|
import PostHeader from "./components/header"
|
||||||
import PostContent from "./components/content"
|
import PostContent from "./components/content"
|
||||||
import PostActions from "./components/actions"
|
import PostActions from "./components/actions"
|
||||||
|
import PostAttachments from "./components/attachments"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
export default React.memo(({
|
export default ({
|
||||||
expansibleActions = window.app.settings.get("postCard_expansible_actions"),
|
expansibleActions = window.app.settings.get("postCard_expansible_actions"),
|
||||||
autoCarrousel = window.app.settings.get("postCard_carrusel_auto"),
|
autoCarrousel = window.app.settings.get("postCard_carrusel_auto"),
|
||||||
data = {},
|
data = {},
|
||||||
@ -160,7 +161,12 @@ export default React.memo(({
|
|||||||
autoCarrousel={autoCarrousel}
|
autoCarrousel={autoCarrousel}
|
||||||
fullmode={fullmode}
|
fullmode={fullmode}
|
||||||
onDoubleClick={onDoubleClick}
|
onDoubleClick={onDoubleClick}
|
||||||
/>
|
nsfw={data.flags && data.flags.includes("nsfw")}
|
||||||
|
>
|
||||||
|
{data.attachments && data.attachments.length > 0 && <PostAttachments
|
||||||
|
attachments={data.attachments}
|
||||||
|
/>}
|
||||||
|
</PostContent>
|
||||||
</div>
|
</div>
|
||||||
{!fullmode &&
|
{!fullmode &&
|
||||||
<div className="post_actionsIndicator">
|
<div className="post_actionsIndicator">
|
||||||
@ -181,4 +187,4 @@ export default React.memo(({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
})
|
}
|
Loading…
x
Reference in New Issue
Block a user