webapp/src/components/Feed/Card.tsx
2022-12-07 19:38:05 +01:00

198 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { t } from '../../utils/intl'
import { createMemo, For, Show } from 'solid-js'
import type { Shout } from '../../graphql/types.gen'
import { capitalize } from '../../utils'
import { translit } from '../../utils/ru2en'
import { Icon } from '../_shared/Icon'
import styles from './Card.module.scss'
import { locale } from '../../stores/ui'
import { clsx } from 'clsx'
import { CardTopic } from './CardTopic'
import { RatingControl } from '../Article/RatingControl'
interface ArticleCardProps {
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
}
article: Shout
}
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 }
}
export const ArticleCard = (props: ArticleCardProps) => {
const mainTopic =
props.article.topics.find((articleTopic) => articleTopic.slug === props.article.mainTopic) ||
props.article.topics[0]
const formattedDate = createMemo<string>(() => {
return new Date(props.article.createdAt)
.toLocaleDateString(locale(), { month: 'long', day: 'numeric', year: 'numeric' })
.replace(' г.', '')
})
const { title, subtitle } = getTitleAndSubtitle(props.article)
const { cover, layout, slug, authors, stat } = props.article
return (
<section
class={clsx(styles.shoutCard, `${props.settings?.additionalClass || ''}`)}
classList={{
[styles.shoutCardShort]: props.settings?.isShort,
[styles.shoutCardPhotoBottom]: props.settings?.noimage && props.settings?.photoBottom,
[styles.shoutCardFeed]: props.settings?.isFeedMode,
[styles.shoutCardFloorImportant]: props.settings?.isFloorImportant,
[styles.shoutCardWithCover]: props.settings?.isWithCover,
[styles.shoutCardBigTitle]: props.settings?.isBigTitle,
[styles.shoutCardVertical]: props.settings?.isVertical,
[styles.shoutCardWithBorder]: props.settings?.withBorder,
[styles.shoutCardCompact]: props.settings?.isCompact,
[styles.shoutCardSingle]: props.settings?.isSingle,
[styles.shoutCardBeside]: props.settings?.isBeside
}}
>
<Show when={!props.settings?.noimage && cover}>
<div class={styles.shoutCardCoverContainer}>
<div class={styles.shoutCardCover}>
<img src={cover || ''} alt={title || ''} loading="lazy" />
</div>
</div>
</Show>
<div class={styles.shoutCardContent}>
<Show when={layout && layout !== 'article' && !(props.settings?.noicon || props.settings?.noimage)}>
<div class={styles.shoutCardType}>
<a href={`/expo/${layout}`}>
<Icon name={layout} class={styles.icon} />
</a>
</div>
</Show>
<Show when={!props.settings?.isGroup}>
<CardTopic
title={
locale() === 'ru' && mainTopic.title ? mainTopic.title : mainTopic?.slug?.replace('-', ' ')
}
slug={mainTopic.slug}
isFloorImportant={props.settings?.isFloorImportant}
/>
</Show>
<div class={styles.shoutCardTitlesContainer}>
<a href={`/${slug || ''}`}>
<div class={styles.shoutCardTitle}>
<span class={styles.shoutCardLinkContainer}>{title}</span>
</div>
<Show when={!props.settings?.nosubtitle && subtitle}>
<div class={styles.shoutCardSubtitle}>
<span class={styles.shoutCardLinkContainer}>{subtitle}</span>
</div>
</Show>
</a>
</div>
<Show when={!props.settings?.noauthor || !props.settings?.nodate}>
<div class={styles.shoutDetails}>
<Show when={!props.settings?.noauthor}>
<div class={styles.shoutAuthor}>
<For each={authors}>
{(author, index) => {
const name =
author.name === 'Дискурс' && locale() !== 'ru' ? 'Discours' : translit(author.name)
return (
<>
<Show when={index() > 0}>, </Show>
<a href={`/author/${author.slug}`}>{name}</a>
</>
)
}}
</For>
</div>
</Show>
<Show when={!props.settings?.nodate}>
<div class={styles.shoutDate}>{formattedDate()}</div>
</Show>
</div>
</Show>
<Show when={props.settings?.isFeedMode}>
<section class={styles.shoutCardDetails}>
<div class={styles.shoutCardDetailsContent}>
<RatingControl rating={stat?.rating} class={styles.shoutCardDetailsItem} />
<div
class={clsx(
styles.shoutCardDetailsItem,
styles.shoutCardDetailsViewed,
styles.shoutCardComments
)}
>
<Icon name="eye" class={styles.icon} />
{stat?.viewed}
</div>
<div class={clsx(styles.shoutCardDetailsItem, styles.shoutCardComments)}>
<a href={`/${slug + '#comments' || ''}`}>
<Icon name="comment" class={styles.icon} />
{stat?.commented || ''}
</a>
</div>
<div class={styles.shoutCardDetailsItem}>
<button>
<Icon name="bookmark" class={styles.icon} />
</button>
</div>
<div class={styles.shoutCardDetailsItem}>
<button>
<Icon name="ellipsis" class={styles.icon} />
</button>
</div>
</div>
<button class={clsx('button--light', styles.shoutCardEditControl)}>{t('Collaborate')}</button>
</section>
</Show>
</div>
</section>
)
}