import type { Author, Shout, Topic } from '../../../graphql/schema/core.gen' import { getPagePath, openPage } from '@nanostores/router' import { clsx } from 'clsx' import { For, Show, createMemo, createSignal } from 'solid-js' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' import { router, useRouter } from '../../../stores/router' import { capitalize } from '../../../utils/capitalize' import { getDescription } from '../../../utils/meta' import { CoverImage } from '../../Article/CoverImage' import { SharePopup, getShareUrl } from '../../Article/SharePopup' import { ShoutRatingControl } from '../../Article/ShoutRatingControl' import { AuthorLink } from '../../Author/AuthorLink' import { Icon } from '../../_shared/Icon' import { Image } from '../../_shared/Image' import { Popover } from '../../_shared/Popover' import { CardTopic } from '../CardTopic' import { FeedArticlePopup } from '../FeedArticlePopup' import stylesHeader from '../../Nav/Header/Header.module.scss' import styles from './ArticleCard.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) } } // TODO: simple fast auto translated title/substitle return { title, subtitle } } const getMainTopicTitle = (article: Shout, lng: string) => { const mainTopicSlug = article.main_topic || '' const mainTopic = article.topics?.find((tpc: Topic) => tpc.slug === mainTopicSlug) const mainTopicTitle = mainTopicSlug && lng === 'en' ? mainTopicSlug.replace(/-/, ' ') : mainTopic?.title || '' return [mainTopicTitle, mainTopicSlug] } const LAYOUT_ASPECT = { music: styles.aspectRatio1x1, literature: styles.aspectRatio16x9, video: styles.aspectRatio16x9, image: styles.aspectRatio4x3, } export const ArticleCard = (props: ArticleCardProps) => { const { t, lang, formatDate } = useLocalize() const { author } = useSession() const { changeSearchParams } = useRouter() const [isActionPopupActive, setIsActionPopupActive] = createSignal(false) const [isCoverImageLoadError, setIsCoverImageLoadError] = createSignal(false) const [isCoverImageLoading, setIsCoverImageLoading] = createSignal(true) const description = getDescription(props.article.body) const aspectRatio = () => LAYOUT_ASPECT[props.article.layout] const [mainTopicTitle, mainTopicSlug] = getMainTopicTitle(props.article, lang()) const { title, subtitle } = getTitleAndSubtitle(props.article) const formattedDate = createMemo(() => props.article?.published_at ? formatDate(new Date(props.article.published_at * 1000)) : '', ) const canEdit = () => props.article.authors?.some((a) => a && a?.slug === author()?.slug) || props.article.created_by?.id === author()?.id const scrollToComments = (event) => { event.preventDefault() openPage(router, 'article', { slug: props.article.slug }) changeSearchParams({ scrollTo: 'comments', }) } return (
} > {title} { setIsCoverImageLoadError(true) setIsCoverImageLoading(false) }} onLoad={() => setIsCoverImageLoading(false)} />
{(a: 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={ } />
) }