import React from "react"
import * as antd from "antd"
import { Swiper } from "antd-mobile"
import { Icons } from "components/Icons"
import { Image, LikeButton } from "components"
import moment from "moment"
import classnames from "classnames"
import loadable from "@loadable/component"
import { processString } from "utils"
import CSSMotion from "rc-animate/lib/CSSMotion"
import useLayoutEffect from "rc-util/lib/hooks/useLayoutEffect"
import "./index.less"
const mediaTypes = {
"jpg": "image",
"jpeg": "image",
"png": "image",
"gif": "image",
"mp4": "video",
"webm": "video",
"ogv": "video",
"mov": "video",
"avi": "video",
"mkv": "video",
"ogg": "audio",
"mp3": "audio",
"wav": "audio",
"flac": "audio",
"aac": "audio",
"m4a": "audio",
}
const ContentFailed = () => {
return
}
const getCurrentHeight = (node) => ({ height: node.offsetHeight })
const getMaxHeight = (node) => {
return { height: node.scrollHeight }
}
const getCollapsedHeight = () => ({ height: 0, opacity: 0 })
function PostHeader(props) {
const [timeAgo, setTimeAgo] = React.useState(0)
const goToProfile = () => {
window.app.goToAccount(props.postData.user?.username)
}
const updateTimeAgo = () => {
setTimeAgo(moment(props.postData.created_at ?? "").fromNow())
}
React.useEffect(() => {
updateTimeAgo()
const interval = setInterval(() => {
updateTimeAgo()
}, 10000)
return () => {
clearInterval(interval)
}
}, [props.postData.created_at])
return
{props.postData.user?.fullName ?? `@${props.postData.user?.username}`}
{props.postData.user?.verified && }
{timeAgo}
}
const PostContent = React.memo((props) => {
let { message, additions } = props.data
let carruselRef = React.useRef(null)
// first filter if is an string
additions = additions.filter(file => typeof file === "string")
// then filter if is an uri
additions = additions.filter(file => /^(http|https):\/\//.test(file))
additions = additions.map((uri, index) => {
const MediaRender = loadable(async () => {
let extension = null
try {
// get media type by parsing the uri
const mediaTypeExt = /\.([a-zA-Z0-9]+)$/.exec(uri)
if (mediaTypeExt) {
extension = mediaTypeExt[1]
} else {
// try to get media by creating requesting the uri
const response = await fetch(uri, {
method: "HEAD",
})
extension = response.headers.get("content-type").split("/")[1]
}
extension = extension.toLowerCase()
const mediaType = mediaTypes[extension]
const mimeType = `${mediaType}/${extension}`
if (!mediaType) {
return () =>
}
switch (mediaType.split("/")[0]) {
case "image": {
return () =>
}
case "video": {
return () =>
}
case "audio": {
return () =>
}
default: {
return () =>
Unsupported media type [{mediaType}/{mediaTypeExt}]
}
}
} catch (error) {
console.error(error)
return () =>
}
})
return
Loading
} >
})
// parse message
const regexs = [
{
regex: /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi,
fn: (key, result) => {
return {result[1]}
}
},
{
regex: /(@[a-zA-Z0-9_]+)/gi,
fn: (key, result) => {
return window.app.setLocation(`/@${result[1].substr(1)}`)}>{result[1]}
}
},
]
message = processString(regexs)(message)
return
{message}
{additions.length > 0 &&
}
prevArrow={
}
autoplay={props.autoCarrousel}
>
{additions}
}
})
const PostActions = (props) => {
const handleSelfMenuAction = async (event) => {
const fn = props.actions[event.key]
if (typeof fn === "function") {
await fn()
}
}
return
}
export const PostCard = React.memo(({
selfId,
expansibleActions = window.app.settings.get("postCard_expansible_actions"),
autoCarrousel = window.app.settings.get("postCard_carrusel_auto"),
data = {},
events = {}
}) => {
const [loading, setLoading] = React.useState(true)
const [likes, setLikes] = React.useState(data.likes ?? [])
const [comments, setComments] = React.useState(data.comments ?? [])
const [hasLiked, setHasLiked] = React.useState(false)
const onClickDelete = async () => {
if (typeof events.onClickDelete !== "function") {
console.warn("onClickDelete event is not a function")
return
}
return await events.onClickDelete(data)
}
const onClickLike = async () => {
if (typeof events.onClickLike !== "function") {
console.warn("onClickLike event is not a function")
return
}
return await events.onClickLike(data)
}
const onDataUpdate = (data) => {
setLikes(data.likes)
setComments(data.comments)
}
React.useEffect(() => {
// first listen to post changes
window.app.ws.listen(`post.dataUpdate.${data._id}`, onDataUpdate)
// proccess post info
// {...}
// then load
setLoading(false)
return () => {
// remove the listener
window.app.ws.unlisten(`post.dataUpdate.${data._id}`, onDataUpdate)
}
}, [])
React.useEffect(() => {
// check if the post has liked by you
const hasLiked = likes.includes(selfId)
//console.log(`[${data._id}] CHECKING LIKE OF USER ${selfId} > ${hasLiked}`)
setHasLiked(hasLiked)
})
if (loading) {
return
}
return
})
export const PostCardAnimated = (props, ref,) => {
const motionRef = React.useRef(false)
useLayoutEffect(() => {
return () => {
if (motionRef.current) {
props.onAppear()
}
}
}, [])
return {
motionRef.current = true
return getMaxHeight(node)
}}
onAppearEnd={props.onAppear}
onLeaveStart={getCurrentHeight}
onLeaveActive={getCollapsedHeight}
onLeaveEnd={() => {
props.onLeave(id)
}}
>
{(_args, passedMotionRef) => {
return
}}
}
export const ForwardedPostCardAnimated = React.forwardRef(PostCardAnimated)
export default PostCard