import { A, useNavigate, useSearchParams } from '@solidjs/router' import { clsx } from 'clsx' import { Accessor, For, Show, createMemo, createSignal } from 'solid-js' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' import type { Author, Maybe, Shout, Topic } from '../../../graphql/schema/core.gen' 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 stylesHeader from '../../Nav/Header/Header.module.scss' import { Icon } from '../../_shared/Icon' import { Image } from '../../_shared/Image' import { Popover } from '../../_shared/Popover' import { CardTopic } from '../CardTopic' import { FeedArticlePopup } from '../FeedArticlePopup' 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?: string // '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: string = article.subtitle || '' if (!subtitle) { let titleParts = article.title?.split('. ') || [] if (titleParts?.length === 1) { titleParts = article.title?.split(/{!|\?|:|;}\s/) || [] } if (titleParts && titleParts.length > 1) { const sep = article.title?.replace(titleParts[0], '').split(' ', 1)[0] title = titleParts[0] + (sep === '.' || sep === ':' ? '' : sep) subtitle = capitalize(article.title?.replace(titleParts[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: Maybe) => tpc?.slug === mainTopicSlug) const mainTopicTitle = mainTopicSlug && lng === 'en' ? mainTopicSlug.replace(/-/, ' ') : mainTopic?.title || '' return [mainTopicTitle, mainTopicSlug] } const LAYOUT_ASPECT: { [key: string]: string } = { music: styles.aspectRatio1x1, audio: styles.aspectRatio1x1, literature: styles.aspectRatio16x9, video: styles.aspectRatio16x9, image: styles.aspectRatio4x3 } export const ArticleCard = (props: ArticleCardProps) => { const { t, lang, formatDate } = useLocalize() const { session } = useSession() const author = createMemo(() => session()?.user?.app_data?.profile as Author) const [, changeSearchParams] = useSearchParams() const [isActionPopupActive, setIsActionPopupActive] = createSignal(false) const [isCoverImageLoadError, setIsCoverImageLoadError] = createSignal(false) const [isCoverImageLoading, setIsCoverImageLoading] = createSignal(true) const description = getDescription(props.article?.body) const aspectRatio: Accessor = () => LAYOUT_ASPECT[props.article?.layout as string] 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 = createMemo( () => Boolean(author()?.id) && (props.article?.authors?.some((a) => Boolean(a) && a?.id === author().id) || props.article?.created_by?.id === author().id || session()?.user?.roles?.includes('editor')) ) const navigate = useNavigate() const scrollToComments = (event: MouseEvent & { currentTarget: HTMLAnchorElement; target: Element }) => { event.preventDefault() navigate(`/article/${props.article.slug}`) changeSearchParams({ scrollTo: 'comments' }) } const onInvite = () => { if (props.onInvite) props.onInvite() } return (
{/* Cover Image */} {/* Cover Image Container */}
} > {title} { setIsCoverImageLoadError(true) setIsCoverImageLoading(false) }} onLoad={() => setIsCoverImageLoading(false)} />
{/* Shout Card Content */}
{/* Shout Card Icon */} {/* Main Topic */} {/* Title and Subtitle */} {/* Details */} {/* Author and Date */}
{(a: Maybe) => ( )}
{/* Description */}
{(triggerRef: (el: HTMLElement) => void) => ( )} {(triggerRef: (el: HTMLElement) => void) => (
)}
{(triggerRef: (el: HTMLElement) => void) => (
setIsActionPopupActive(isVisible)} trigger={ } />
)}
props.onShare?.(props.article)} onInviteClick={onInvite} onVisibilityChange={(isVisible) => setIsActionPopupActive(isVisible)} trigger={ } />
) }