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
Avatar

{props.postData.user?.fullName ?? `@${props.postData.user?.username}`} {props.postData.user?.verified && }

{timeAgo}
{props.likes}
{props.comments}
} 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
{props.isSelf &&
} key="edit"> Edit } key="delete"> Delete } trigger={["click"]} >
}
} 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