import { createMemo, createSignal, For, Show } from 'solid-js' import sanitizeHtml from 'sanitize-html' import type { Shout } from '../../../graphql/types.gen' import { getPagePath, openPage } from '@nanostores/router' import { clsx } from 'clsx' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' import { router, useRouter } from '../../../stores/router' import { showModal } from '../../../stores/ui' import { capitalize } from '../../../utils/capitalize' import { getDescription } from '../../../utils/meta' import { Icon } from '../../_shared/Icon' import { Image } from '../../_shared/Image' import { InviteCoAuthorsModal } from '../../_shared/InviteCoAuthorsModal' import { Popover } from '../../_shared/Popover' import { CoverImage } from '../../Article/CoverImage' import { getShareUrl, SharePopup } from '../../Article/SharePopup' import { ShoutRatingControl } from '../../Article/ShoutRatingControl' import { AuthorLink } from '../../Author/AhtorLink' import { CardTopic } from '../CardTopic' import { FeedArticlePopup } from '../FeedArticlePopup' import styles from './ArticleCard.module.scss' import stylesHeader from '../../Nav/Header/Header.module.scss' export type ArticleCardProps = { // TODO: refactor this, please settings?: { noicon?: boolean noimage?: boolean nosubtitle?: boolean noauthor?: boolean nodate?: boolean isGroup?: boolean photoBottom?: boolean additionalClass?: string isFeedMode?: boolean isFloorImportant?: boolean isWithCover?: boolean isBigTitle?: boolean isVertical?: boolean isShort?: boolean withBorder?: boolean isCompact?: boolean isSingle?: boolean isBeside?: boolean withViewed?: boolean noAuthorLink?: boolean } withAspectRatio?: boolean desktopCoverSize?: 'XS' | 'S' | 'M' | 'L' article: Shout onShare?: (article: Shout) => void onInvite?: () => void } const desktopCoverImageWidths: Record = { XS: 300, S: 400, M: 600, L: 800, } const getTitleAndSubtitle = ( article: Shout, ): { title: string subtitle: string } => { let title = article.title let subtitle = article.subtitle if (!subtitle) { let tt = article.title?.split('. ') || [] if (tt?.length === 1) { tt = article.title?.split(/{!|\?|:|;}\s/) || [] } if (tt && tt.length > 1) { const sep = article.title?.replace(tt[0], '').split(' ', 1)[0] title = tt[0] + (sep === '.' || sep === ':' ? '' : sep) subtitle = capitalize(article.title?.replace(tt[0] + sep, ''), true) } } return { title, subtitle } } const sanitizeString = (html) => sanitizeHtml(html, { allowedTags: ['span'], allowedAttributes: { span: ['class'], }, }) export const ArticleCard = (props: ArticleCardProps) => { const { t, lang, formatDate } = useLocalize() const { user } = useSession() const mainTopic = props.article.topics.find((articleTopic) => articleTopic.slug === props.article.mainTopic) || props.article.topics[0] const formattedDate = createMemo(() => { return formatDate(new Date(props.article.createdAt)) }) const { title, subtitle } = getTitleAndSubtitle(props.article) const canEdit = () => props.article.authors?.some((a) => a.slug === user()?.slug) const { changeSearchParams } = useRouter() const scrollToComments = (event) => { event.preventDefault() openPage(router, 'article', { slug: props.article.slug }) changeSearchParams({ scrollTo: 'comments', }) } const [isActionPopupActive, setIsActionPopupActive] = createSignal(false) const [isCoverImageLoadError, setIsCoverImageLoadError] = createSignal(false) const [isCoverImageLoading, setIsCoverImageLoading] = createSignal(true) const description = getDescription(props.article.body) const aspectRatio = () => { switch (props.article.layout) { case 'music': { return styles.aspectRatio1x1 } case 'image': { return styles.aspectRatio4x3 } case 'video': case 'literature': { return styles.aspectRatio16x9 } } } return (
} > {title} { setIsCoverImageLoadError(true) setIsCoverImageLoading(false) }} onLoad={() => setIsCoverImageLoading(false)} />
{(author) => { return ( ) }}
{(triggerRef: (el) => void) => ( )} {(triggerRef: (el) => void) => (
)}
{(triggerRef: (el) => void) => (
setIsActionPopupActive(isVisible)} trigger={ } />
)}
props.onShare(props.article)} onInviteClick={props.onInvite} onVisibilityChange={(isVisible) => setIsActionPopupActive(isVisible)} trigger={ } />
) }