From fac6422f2af9e35d86ac43867c43b8666acb015a Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:04:09 +0300 Subject: [PATCH] Feature/entity card (#246) Add Bages components --- public/locales/en/translation.json | 27 ++-- public/locales/ru/translation.json | 35 ++--- src/components/AuthGuard/AuthGuard.tsx | 2 +- .../AuthorBadge/AuthorBadge.module.scss | 32 +++++ .../Author/AuthorBadge/AuthorBadge.tsx | 92 ++++++++++++ src/components/Author/AuthorBadge/index.ts | 1 + .../Author/AuthorCard/AuthorCard.module.scss | 6 +- .../Author/AuthorCard/AuthorCard.tsx | 133 +++++++----------- .../Author/Userpic/Userpic.module.scss | 15 +- src/components/Author/Userpic/Userpic.tsx | 3 +- src/components/Feed/ArticleCard.tsx | 2 +- src/components/Feed/Beside.tsx | 16 +-- src/components/Nav/Header/Header.tsx | 2 +- .../Topic/TopicBadge/TopicBadge.module.scss | 60 ++++++++ .../Topic/TopicBadge/TopicBadge.tsx | 111 +++++++++++++++ src/components/Topic/TopicBadge/index.ts | 1 + src/components/Views/Edit.tsx | 2 +- .../ProfileSubscriptions.tsx | 23 +-- .../_shared/Button/Button.module.scss | 6 +- .../CheckButton/CheckButton.module.scss | 1 + src/graphql/query/articles-load-by.ts | 2 + src/graphql/types.gen.ts | 16 +-- src/pages/profile/profileSettings.page.tsx | 2 +- src/styles/app.scss | 2 + 24 files changed, 423 insertions(+), 169 deletions(-) create mode 100644 src/components/Author/AuthorBadge/AuthorBadge.module.scss create mode 100644 src/components/Author/AuthorBadge/AuthorBadge.tsx create mode 100644 src/components/Author/AuthorBadge/index.ts create mode 100644 src/components/Topic/TopicBadge/TopicBadge.module.scss create mode 100644 src/components/Topic/TopicBadge/TopicBadge.tsx create mode 100644 src/components/Topic/TopicBadge/index.ts diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 3cc4a2dc..910abebc 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -9,11 +9,9 @@ "Add audio": "Add audio", "Add blockquote": "Add blockquote", "Add comment": "Comment", - "Here you can manage all your Discourse subscriptions": "Here you can manage all your Discourse subscriptions", "Add cover": "Add cover", "Add image": "Add image", "Add images": "Add images", - "Collections": "Collections", "Add intro": "Add intro", "Add link": "Add link", "Add rule": "Add rule", @@ -65,6 +63,7 @@ "Choose a title image for the article. You can immediately see how the publication card will look like.": "Choose a title image for the article. You can immediately see how the publication card will look like.", "Choose who you want to write to": "Choose who you want to write to", "Collaborate": "Help Edit", + "Collections": "Collections", "Come up with a subtitle for your story": "Come up with a subtitle for your story", "Come up with a title for your story": "Come up with a title for your story", "Comment successfully deleted": "Comment successfully deleted", @@ -147,6 +146,7 @@ "Help": "Помощь", "Help to edit": "Help to edit", "Here you can customize your profile the way you want.": "Here you can customize your profile the way you want.", + "Here you can manage all your Discourse subscriptions": "Here you can manage all your Discourse subscriptions", "Hide table of contents": "Hide table of contents", "Highlight": "Highlight", "Hooray! Welcome!": "Hooray! Welcome!", @@ -154,8 +154,8 @@ "Hot topics": "Hot topics", "Hotkeys": "Горячие клавиши", "How can I help/skills": "How can I help/skills", - "How to help": "How to help?", "How it works": "How it works", + "How to help": "How to help?", "How to write a good article": "Как написать хорошую статью", "How to write an article": "How to write an article", "I have an account": "I have an account!", @@ -202,16 +202,17 @@ "My feed": "My feed", "My subscriptions": "Subscriptions", "Name": "Name", - "Newsletter": "Newsletter", "New literary work": "New literary work", "New only": "New only", "New password": "New password", "New stories every day and even more!": "New stories and more are waiting for you every day!", + "Newsletter": "Newsletter", "Night mode": "Night mode", "No such account, please try to register": "No such account found, please try to register", "Nothing here yet": "There's nothing here yet", "Nothing is here": "There is nothing here", "Notifications": "Notifications", + "Registered since {{date}}": "Registered since {{date}}", "Or continue with social network": "Or continue with social network", "Or paste a link to an image": "Or paste a link to an image", "Ordered list": "Ordered list", @@ -285,9 +286,9 @@ "Start conversation": "Start a conversation", "Subsccriptions": "Subscriptions", "Subscribe": "Subscribe", + "Subscribe us": "Subscribe us", "Subscribe what you like to tune your personal feed": "Subscribe to topics that interest you to customize your personal feed and get instant updates on new posts and discussions", "Subscribe who you like to tune your personal feed": "Subscribe to authors you're interested in to customize your personal feed and get instant updates on new posts and discussions", - "Subscribe us": "Subscribe us", "Subscription": "Subscription", "Subscriptions": "Subscriptions", "Substrate": "Substrate", @@ -328,6 +329,7 @@ "Upload video": "Upload video", "Username": "Username", "Userpic": "Userpic", + "Users": "Users", "Video": "Video", "Video format not supported": "Video format not supported", "Views": "Views", @@ -351,8 +353,8 @@ "You ll be able to participate in discussions, rate others' comments and learn about new responses": "You ll be able to participate in discussions, rate others' comments and learn about new responses", "You've confirmed email": "You've confirmed email", "You've reached a non-existed page": "You've reached a non-existed page", - "Your name will appear on your profile page and as your signature in publications, comments and responses.": "Your name will appear on your profile page and as your signature in publications, comments and responses", "Your email": "Your email", + "Your name will appear on your profile page and as your signature in publications, comments and responses.": "Your name will appear on your profile page and as your signature in publications, comments and responses", "accomplices": "accomplices", "actions": "actions", "add link": "add link", @@ -395,17 +397,16 @@ "shout": "post", "sign up or sign in": "sign up or sign in", "slug is used by another user": "Slug is already taken by another user", - "terms of use": "terms of use", - "topics": "topics", - "user already exist": "user already exists", - "video": "video", - "view": "view", - "zine": "zine", "subscriber": "subscriber", "subscriber_rp": "subscriber", "subscribers": "subscribers", "subscription": "subscription", "subscription_rp": "subscription", "subscriptions": "subscriptions", - "Users": "Users" + "terms of use": "terms of use", + "topics": "topics", + "user already exist": "user already exists", + "video": "video", + "view": "view", + "zine": "zine" } diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 930a5f30..5ecc804f 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -67,6 +67,7 @@ "Choose a title image for the article. You can immediately see how the publication card will look like.": "Выберите заглавное изображение для статьи. Тут же сразу можно увидеть как будет выглядеть карточка публикации.", "Choose who you want to write to": "Выберите кому хотите написать", "Collaborate": "Помочь редактировать", + "Collections": "Коллекции", "Come up with a subtitle for your story": "Придумайте подзаголовок вашей истории", "Come up with a title for your story": "Придумайте заголовок вашей истории", "Comment successfully deleted": "Комментарий успешно удален", @@ -75,7 +76,6 @@ "Confirm": "Подтвердить", "Cooperate": "Соучаствовать", "Copy": "Скопировать", - "Collections": "Коллекции", "Copy link": "Скопировать ссылку", "Corrections history": "История правок", "Create Chat": "Создать чат", @@ -111,6 +111,7 @@ "Email": "Почта", "Enter": "Войти", "Enter URL address": "Введите адрес ссылки", + "Enter footnote text": "Введите текст сноски", "Enter image description": "Введите описание изображения", "Enter image title": "Введите название изображения", "Enter text": "Введите текст", @@ -153,6 +154,7 @@ "Help": "Помощь", "Help to edit": "Помочь редактировать", "Here you can customize your profile the way you want.": "Здесь можно настроить свой профиль так, как вы хотите.", + "Here you can manage all your Discourse subscriptions": "Здесь можно управлять всеми своими подписками на Дискурсе", "Hide table of contents": "Скрыть главление", "Highlight": "Подсветка", "Hooray! Welcome!": "Ура! Добро пожаловать!", @@ -209,18 +211,18 @@ "Move up": "Переместить вверх", "My feed": "Моя лента", "My subscriptions": "Подписки", - "Here you can manage all your Discourse subscriptions": "Здесь можно управлять всеми своими подписками на Дискурсе", "Name": "Имя", - "Newsletter": "Рассылка", "New literary work": "Новое произведение", "New only": "Только новые", "New password": "Новый пароль", "New stories every day and even more!": "Каждый день вас ждут новые истории и ещё много всего интересного!", + "Newsletter": "Рассылка", "Night mode": "Ночная тема", "No such account, please try to register": "Такой адрес не найден, попробуйте зарегистрироваться", "Nothing here yet": "Здесь пока ничего нет", "Nothing is here": "Здесь ничего нет", "Notifications": "Уведомления", + "Registered since {{date}}": "На сайте c {{date}}", "Or continue with social network": "Или войдите через соцсеть", "Or paste a link to an image": "Или вставьте ссылку на изображение", "Ordered list": "Нумерованный список", @@ -286,8 +288,8 @@ "Share": "Поделиться", "Short opening": "Расскажите вашу историю...", "Show": "Показать", - "Show more": "Читать дальше", "Show lyrics": "Текст песни", + "Show more": "Читать дальше", "Show table of contents": "Показать главление", "Slug": "Постоянная ссылка", "Social networks": "Социальные сети", @@ -302,11 +304,10 @@ "Subheader": "Подзаголовок", "Subscribe": "Подписаться", "Subscribe to comments": "Подписаться на комментарии", + "Subscribe us": "Подпишитесь на нас", "Subscribe what you like to tune your personal feed": "Подпишитесь на интересующие вас темы, чтобы настроить вашу персональную ленту и моментально узнавать о новых публикациях и обсуждениях", "Subscribe who you like to tune your personal feed": "Подпишитесь на интересующих вас авторов, чтобы настроить вашу персональную ленту и моментально узнавать о новых публикациях и обсуждениях", - "Subscribe us": "Подпишитесь на нас", "Subscription": "Подписка", - "subscription": "подписка", "Subscriptions": "Подписки", "Substrate": "Подложка", "Success": "Успешно", @@ -340,12 +341,13 @@ "Try to find another way": "Попробуйте найти по-другому", "Unfollow": "Отписаться", "Unfollow the topic": "Отписаться от темы", - "Unnamed draft": "Unnamed draft", + "Unnamed draft": "Черновик без названия", "Upload": "Загрузить", "Upload error": "Ошибка загрузки", "Upload video": "Загрузить видео", "Username": "Имя пользователя", "Userpic": "Аватар", + "Users": "Пользователи", "Video": "Видео", "Video format not supported": "Тип видео не поддерживается", "Views": "Просмотры", @@ -371,8 +373,8 @@ "You've confirmed email": "Вы подтвердили почту", "You've reached a non-existed page": "Вы попали на несуществующую страницу", "You've successfully logged out": "Вы успешно вышли из аккаунта", - "Your name will appear on your profile page and as your signature in publications, comments and responses.": "Ваше имя появится на странице вашего профиля и как ваша подпись в публикациях, комментариях и откликах", "Your email": "Ваш email", + "Your name will appear on your profile page and as your signature in publications, comments and responses.": "Ваше имя появится на странице вашего профиля и как ваша подпись в публикациях, комментариях и откликах", "actions": "действия", "add link": "добавить ссылку", "all topics": "все темы", @@ -395,6 +397,7 @@ "email not confirmed": "email не подтвержден", "enter": "войдите", "feed": "лента", + "follower": "подписчик", "general feed": "Общая лента", "header 1": "заголовок 1", "header 2": "заголовок 2", @@ -420,18 +423,16 @@ "sign up or sign in": "зарегистрироваться или войти", "slug is used by another user": "Имя уже занято другим пользователем", "squib": "Подверстка", + "subscriber": "подписчик", + "subscriber_rp": "подписчика", + "subscribers": "подписчиков", + "subscription": "подписка", + "subscription_rp": "подписки", + "subscriptions": "подписок", "terms of use": "правилами пользования сайтом", "topics": "темы", "user already exist": "пользователь уже существует", "video": "видео", "view": "просмотр", - "zine": "журнал", - "Enter footnote text": "Введите текст сноски", - "follower": "подписчик", - "subscriber": "подписчик", - "subscriber_rp": "подписчика", - "subscribers": "подписчиков", - "subscription_rp": "подписки", - "subscriptions": "подписок", - "Users": "Пользователи" + "zine": "журнал" } diff --git a/src/components/AuthGuard/AuthGuard.tsx b/src/components/AuthGuard/AuthGuard.tsx index 5a2394e2..c9d50c20 100644 --- a/src/components/AuthGuard/AuthGuard.tsx +++ b/src/components/AuthGuard/AuthGuard.tsx @@ -1,6 +1,6 @@ import { createEffect, JSX, Show } from 'solid-js' import { useSession } from '../../context/session' -import { hideModal, showModal } from '../../stores/ui' +import { hideModal } from '../../stores/ui' import { useRouter } from '../../stores/router' import { RootSearchParams } from '../../pages/types' import { AuthModalSearchParams } from '../Nav/AuthModal/types' diff --git a/src/components/Author/AuthorBadge/AuthorBadge.module.scss b/src/components/Author/AuthorBadge/AuthorBadge.module.scss new file mode 100644 index 00000000..2fc2ead4 --- /dev/null +++ b/src/components/Author/AuthorBadge/AuthorBadge.module.scss @@ -0,0 +1,32 @@ +.AuthorBadge { + display: flex; + flex-flow: row nowrap; + align-items: flex-start; + margin-bottom: 1rem; + + .info { + @include font-size(1.4rem); + + display: flex; + flex-direction: column; + border: none; + + &:hover { + background: unset; + } + + .name { + color: var(--default-color); + font-weight: 500; + } + + .bio { + color: var(--black-400); + } + } + + .actions { + margin-left: auto; + padding-left: 1rem; + } +} diff --git a/src/components/Author/AuthorBadge/AuthorBadge.tsx b/src/components/Author/AuthorBadge/AuthorBadge.tsx new file mode 100644 index 00000000..c2414246 --- /dev/null +++ b/src/components/Author/AuthorBadge/AuthorBadge.tsx @@ -0,0 +1,92 @@ +import { clsx } from 'clsx' +import styles from './AuthorBadge.module.scss' +import { Userpic } from '../Userpic' +import { Author, FollowingEntity } from '../../../graphql/types.gen' +import { createMemo, createSignal, Show } from 'solid-js' +import { formatDate } from '../../../utils' +import { useLocalize } from '../../../context/localize' +import { Button } from '../../_shared/Button' +import { useSession } from '../../../context/session' +import { follow, unfollow } from '../../../stores/zine/common' +import { CheckButton } from '../../_shared/CheckButton' + +type Props = { + author: Author + minimizeSubscribeButton?: boolean +} +export const AuthorBadge = (props: Props) => { + const [isSubscribing, setIsSubscribing] = createSignal(false) + const { + isAuthenticated, + session, + actions: { loadSession } + } = useSession() + + const { t } = useLocalize() + const subscribed = createMemo(() => { + return session()?.news?.authors?.some((u) => u === props.author.slug) || false + }) + + const subscribe = async (really = true) => { + setIsSubscribing(true) + + await (really + ? follow({ what: FollowingEntity.Author, slug: props.author.slug }) + : unfollow({ what: FollowingEntity.Author, slug: props.author.slug })) + + await loadSession() + setIsSubscribing(false) + } + + return ( +
+ + +
{props.author.name}
+ + {t('Registered since {{date}}', { date: formatDate(new Date(props.author.createdAt)) })} +
+ } + > +
{props.author.bio}
+ +
+ +
+ subscribe(!subscribed)} + /> + } + > + subscribe(true)} + /> + } + > +
+
+ + ) +} diff --git a/src/components/Author/AuthorBadge/index.ts b/src/components/Author/AuthorBadge/index.ts new file mode 100644 index 00000000..67fae98d --- /dev/null +++ b/src/components/Author/AuthorBadge/index.ts @@ -0,0 +1 @@ +export { AuthorBadge } from './AuthorBadge' diff --git a/src/components/Author/AuthorCard/AuthorCard.module.scss b/src/components/Author/AuthorCard/AuthorCard.module.scss index 6816bae0..e3b9ccc7 100644 --- a/src/components/Author/AuthorCard/AuthorCard.module.scss +++ b/src/components/Author/AuthorCard/AuthorCard.module.scss @@ -1,6 +1,6 @@ .author { - align-items: center; display: flex; + align-items: center; flex-flow: row nowrap; margin-bottom: 1.6rem; @@ -600,8 +600,10 @@ align-items: center; margin-bottom: 0.4rem; - .authorName { + .authorName, + .authorAbout { @include font-size(1.2rem); + margin-bottom: 0; } diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index 20333471..d0ada63b 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -15,11 +15,11 @@ import { useLocalize } from '../../../context/localize' import { ConditionalWrapper } from '../../_shared/ConditionalWrapper' import { Modal } from '../../Nav/Modal' import { showModal } from '../../../stores/ui' -import { TopicCard } from '../../Topic/Card' import { getNumeralsDeclension } from '../../../utils/getNumeralsDeclension' import { SubscriptionFilter } from '../../../pages/types' import { isAuthor } from '../../../utils/isAuthor' -import { CheckButton } from '../../_shared/CheckButton' +import { AuthorBadge } from '../AuthorBadge' +import { TopicBadge } from '../../Topic/TopicBadge' type Props = { caption?: string @@ -42,7 +42,6 @@ type Props = { followers?: Author[] following?: Array showPublicationsCounter?: boolean - minimizeSubscribeButton?: boolean } export const AuthorCard = (props: Props) => { @@ -285,35 +284,12 @@ export const AuthorCard = (props: Props) => { - - - - - - - - {t('Follow')} - - - - } - > + + subscribe(false)} + onClick={handleSubscribe} class={clsx('button', styles.button)} classList={{ [styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton, @@ -325,37 +301,52 @@ export const AuthorCard = (props: Props) => { disabled={isSubscribing()} > - + - - {t('Unfollow')} - - - {t('You are subscribed')} + + {t('Follow')} - - - - + @@ -390,17 +381,7 @@ export const AuthorCard = (props: Props) => {
- {(follower: Author) => ( - - )} + {(follower: Author) => }
@@ -444,23 +425,9 @@ export const AuthorCard = (props: Props) => { {(subscription) => isAuthor(subscription) ? ( - + ) : ( - + ) } diff --git a/src/components/Author/Userpic/Userpic.module.scss b/src/components/Author/Userpic/Userpic.module.scss index 9059a66f..07b0bd89 100644 --- a/src/components/Author/Userpic/Userpic.module.scss +++ b/src/components/Author/Userpic/Userpic.module.scss @@ -3,14 +3,14 @@ background: #f7f7f8; border-radius: 100%; display: flex; - height: 32px; justify-content: center; margin-right: 1.2rem; min-width: 32px; max-width: 32px; + height: 32px; + width: 32px; overflow: hidden; position: relative; - width: 32px; img { height: 100%; @@ -55,6 +55,17 @@ } } + &.medium { + width: 40px; + height: 40px; + min-width: 40px; + max-width: 40px; + + .letters { + line-height: 40px; + } + } + &.big { aspect-ratio: 1/1; margin: 0 auto; diff --git a/src/components/Author/Userpic/Userpic.tsx b/src/components/Author/Userpic/Userpic.tsx index ef0f1fdb..578f8f8f 100644 --- a/src/components/Author/Userpic/Userpic.tsx +++ b/src/components/Author/Userpic/Userpic.tsx @@ -13,8 +13,8 @@ type Props = { onClick?: () => void loading?: boolean isBig?: boolean + isMedium?: boolean hasLink?: boolean - isAuthorsList?: boolean isFeedMode?: boolean } @@ -30,6 +30,7 @@ export const Userpic = (props: Props) => {
{
-
{ author={author} hideWriteButton={true} hideFollow={true} + truncateBio={true} isFeedMode={true} hasLink={!props.settings?.noAuthorLink} /> diff --git a/src/components/Feed/Beside.tsx b/src/components/Feed/Beside.tsx index c5788d0b..160eefae 100644 --- a/src/components/Feed/Beside.tsx +++ b/src/components/Feed/Beside.tsx @@ -2,16 +2,15 @@ import { For, Show } from 'solid-js' import { ArticleCard } from './ArticleCard' -import { AuthorCard } from '../Author/AuthorCard' import { TopicCard } from '../Topic/Card' import styles from './Beside.module.scss' import type { Author, Shout, Topic, User } from '../../graphql/types.gen' import { Icon } from '../_shared/Icon' - import { clsx } from 'clsx' import { useLocalize } from '../../context/localize' +import { AuthorBadge } from '../Author/AuthorBadge' -interface BesideProps { +type Props = { title?: string values: (Shout | User | Topic | Author)[] beside: Shout @@ -24,7 +23,7 @@ interface BesideProps { nodate?: boolean } -export const Beside = (props: BesideProps) => { +export const Beside = (props: Props) => { const { t } = useLocalize() return ( 0}> @@ -76,14 +75,7 @@ export const Beside = (props: BesideProps) => { /> - + { actions: { requireAuthentication } } = useSession() - const { page, searchParams } = useRouter() + const { searchParams } = useRouter() const [randomTopics, setRandomTopics] = createSignal([]) const [getIsScrollingBottom, setIsScrollingBottom] = createSignal(false) diff --git a/src/components/Topic/TopicBadge/TopicBadge.module.scss b/src/components/Topic/TopicBadge/TopicBadge.module.scss new file mode 100644 index 00000000..17c1088f --- /dev/null +++ b/src/components/Topic/TopicBadge/TopicBadge.module.scss @@ -0,0 +1,60 @@ +.TopicBadge { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 1rem; + margin-bottom: 1rem; + + .picture { + display: block; + width: 40px; + height: 40px; + min-width: 40px; + border-radius: 8px; + background-color: var(--black-50); + background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5LjUgMTQuMjVDMTkuOTE0MSAxNC4yNSAyMC4yNSAxNC41ODU5IDIwLjI1IDE1QzIwLjI1IDE1LjQxNDEgMTkuOTE0MSAxNS43NSAxOS41IDE1Ljc1SDE1Ljc1VjE5LjVDMTUuNzUgMTkuOTE0MSAxNS40MTQxIDIwLjI1IDE1IDIwLjI1QzE0LjU4NTkgMjAuMjUgMTQuMjUgMTkuOTE0MSAxNC4yNSAxOS41VjE1Ljc1SDkuNzVWMTkuNUM5Ljc1IDE5LjkxNDEgOS40MTQwNiAyMC4yNSA5IDIwLjI1QzguNTg1OTQgMjAuMjUgOC4yNSAxOS45MTQxIDguMjUgMTkuNVYxNS43NUg0LjVDNC4wODU5NCAxNS43NSAzLjc1IDE1LjQxNDEgMy43NSAxNUMzLjc1IDE0LjU4NTkgNC4wODU5NCAxNC4yNSA0LjUgMTQuMjVIOC4yNVY5Ljc1SDQuNUM0LjA4NTk0IDkuNzUgMy43NSA5LjQxNDA2IDMuNzUgOUMzLjc1IDguNTg1OTQgNC4wODU5NCA4LjI1IDQuNSA4LjI1SDguMjVWNC41QzguMjUgNC4wODU5NCA4LjU4NTk0IDMuNzUgOSAzLjc1QzkuNDE0MDYgMy43NSA5Ljc1IDQuMDg1OTQgOS43NSA0LjVWOC4yNUgxNC4yNVY0LjVDMTQuMjUgNC4wODU5NCAxNC41ODU5IDMuNzUgMTUgMy43NUMxNS40MTQxIDMuNzUgMTUuNzUgNC4wODU5NCAxNS43NSA0LjVWOC4yNUgxOS41QzE5LjkxNDEgOC4yNSAyMC4yNSA4LjU4NTk0IDIwLjI1IDlDMjAuMjUgOS40MTQwNiAxOS45MTQxIDkuNzUgMTkuNSA5Ljc1SDE1Ljc1VjE0LjI1SDE5LjVaTTkuNzUgMTQuMjVIMTQuMjVWOS43NUg5Ljc1VjE0LjI1WiIgZmlsbD0iIzlGQTFBNyIvPgo8L3N2Zz4K'); + background-position: 50% 50%; + background-repeat: no-repeat; + border: none; + + &:hover { + background-color: var(--black-50); + } + + &.withImage { + background-size: cover; + } + } + + .info { + display: flex; + flex-direction: column; + border: none; + @include font-size(1.4rem); + + &:hover { + background: unset; + } + + .title { + color: var(--blue-500); + font-weight: 700; + } + + .description { + color: var(--black-400); + } + + .stat { + display: flex; + gap: 1rem; + flex-direction: row; + color: var(--default-color); + } + } + + .actions { + margin-left: auto; + padding-left: 1rem; + } +} diff --git a/src/components/Topic/TopicBadge/TopicBadge.tsx b/src/components/Topic/TopicBadge/TopicBadge.tsx new file mode 100644 index 00000000..28368b66 --- /dev/null +++ b/src/components/Topic/TopicBadge/TopicBadge.tsx @@ -0,0 +1,111 @@ +import { clsx } from 'clsx' +import styles from './TopicBadge.module.scss' +import { FollowingEntity, Topic } from '../../../graphql/types.gen' +import { createMemo, createSignal, Show } from 'solid-js' +import { imageProxy } from '../../../utils/imageProxy' +import { capitalize } from '../../../utils' +import { Button } from '../../_shared/Button' +import { useSession } from '../../../context/session' +import { useLocalize } from '../../../context/localize' +import { follow, unfollow } from '../../../stores/zine/common' +import { CheckButton } from '../../_shared/CheckButton' + +type Props = { + topic: Topic + minimizeSubscribeButton?: boolean +} + +export const TopicBadge = (props: Props) => { + const [isSubscribing, setIsSubscribing] = createSignal(false) + const { t } = useLocalize() + const { + isAuthenticated, + session, + actions: { loadSession } + } = useSession() + + const subscribed = createMemo(() => { + if (!session()?.user?.slug || !session()?.news?.topics) { + return false + } + + return session()?.news.topics.includes(props.topic.slug) + }) + + const subscribe = async (really = true) => { + setIsSubscribing(true) + + await (really + ? follow({ what: FollowingEntity.Topic, slug: props.topic.slug }) + : unfollow({ what: FollowingEntity.Topic, slug: props.topic.slug })) + + await loadSession() + setIsSubscribing(false) + } + + return ( +
+ + + {capitalize(props.topic.title)} + +
{props.topic.body}
+
+ + +
+ {t('Authors')}: {props.topic.stat.authors} +
+
+ +
+ {t('Followers')}: {props.topic.stat.followers} +
+
+ +
+ {t('Publications')}: {props.topic.stat.shouts} +
+
+
+
+ +
+ subscribe(!subscribed)} + /> + } + > + subscribe(true)} + /> + } + > +
+
+
+ ) +} diff --git a/src/components/Topic/TopicBadge/index.ts b/src/components/Topic/TopicBadge/index.ts new file mode 100644 index 00000000..3dfc04c1 --- /dev/null +++ b/src/components/Topic/TopicBadge/index.ts @@ -0,0 +1 @@ +export { TopicBadge } from './TopicBadge' diff --git a/src/components/Views/Edit.tsx b/src/components/Views/Edit.tsx index 48b1534d..abc7a64c 100644 --- a/src/components/Views/Edit.tsx +++ b/src/components/Views/Edit.tsx @@ -1,4 +1,4 @@ -import { Accessor, createEffect, createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js' +import { Accessor, createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js' import { useLocalize } from '../../context/localize' import { clsx } from 'clsx' import { Title } from '@solidjs/meta' diff --git a/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx b/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx index b3d121a3..10afd39a 100644 --- a/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx +++ b/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx @@ -1,12 +1,9 @@ import { clsx } from 'clsx' -// import styles from './ProfileSubscriptions.module.scss' import { ProfileSettingsNavigation } from '../../Nav/ProfileSettingsNavigation' import { createEffect, createSignal, For, onMount, Show } from 'solid-js' import { Loading } from '../../_shared/Loading' import { SearchField } from '../../_shared/SearchField' import { isAuthor } from '../../../utils/isAuthor' -import { AuthorCard } from '../../Author/AuthorCard' -import { TopicCard } from '../../Topic/Card' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' import { Author, Topic } from '../../../graphql/types.gen' @@ -16,6 +13,8 @@ import { dummyFilter } from '../../../utils/dummyFilter' // TODO: refactor styles import styles from '../../../pages/profile/Settings.module.scss' import stylesSettings from '../../../styles/FeedSettings.module.scss' +import { AuthorBadge } from '../../Author/AuthorBadge' +import { TopicBadge } from '../../Topic/TopicBadge' export const ProfileSubscriptions = () => { const { t, lang } = useLocalize() @@ -104,23 +103,9 @@ export const ProfileSubscriptions = () => { {(followingItem) => (
{isAuthor(followingItem) ? ( - + ) : ( - + )}
)} diff --git a/src/components/_shared/Button/Button.module.scss b/src/components/_shared/Button/Button.module.scss index 6b7c32e4..b6b3dfd8 100644 --- a/src/components/_shared/Button/Button.module.scss +++ b/src/components/_shared/Button/Button.module.scss @@ -5,6 +5,7 @@ justify-content: center; font-weight: 500; cursor: pointer; + white-space: nowrap; &.primary { background: #000; @@ -57,7 +58,10 @@ margin-right: 0.8em; min-width: auto !important; padding: 0; - transition: border-color 0.3s, background-color 0.3s, color 0.3s; + transition: + border-color 0.3s, + background-color 0.3s, + color 0.3s; &:hover, &:active { diff --git a/src/components/_shared/CheckButton/CheckButton.module.scss b/src/components/_shared/CheckButton/CheckButton.module.scss index dd1a491b..d62742f1 100644 --- a/src/components/_shared/CheckButton/CheckButton.module.scss +++ b/src/components/_shared/CheckButton/CheckButton.module.scss @@ -4,6 +4,7 @@ justify-content: center; height: 32px; min-width: 33px; + line-height: 32px; box-sizing: border-box; padding: 0 8px; background: var(--background-color); diff --git a/src/graphql/query/articles-load-by.ts b/src/graphql/query/articles-load-by.ts index 5db2edca..673b8292 100644 --- a/src/graphql/query/articles-load-by.ts +++ b/src/graphql/query/articles-load-by.ts @@ -30,6 +30,8 @@ export default gql` name slug userpic + createdAt + bio } createdAt publishedAt diff --git a/src/graphql/types.gen.ts b/src/graphql/types.gen.ts index 61516a6b..86e32fb3 100644 --- a/src/graphql/types.gen.ts +++ b/src/graphql/types.gen.ts @@ -25,6 +25,7 @@ export type Author = { about?: Maybe bio?: Maybe caption?: Maybe + createdAt?: Maybe id: Scalars['Int'] lastSeen?: Maybe links?: Maybe>> @@ -116,6 +117,7 @@ export type LoadShoutsFilters = { author?: InputMaybe body?: InputMaybe days?: InputMaybe + excludeLayout?: InputMaybe layout?: InputMaybe reacted?: InputMaybe title?: InputMaybe @@ -589,20 +591,6 @@ export type ShoutInput = { topics?: InputMaybe>> } -export type ShoutsFilterBy = { - author?: InputMaybe - authors?: InputMaybe>> - body?: InputMaybe - days?: InputMaybe - layout?: InputMaybe - slug?: InputMaybe - stat?: InputMaybe - title?: InputMaybe - topic?: InputMaybe - topics?: InputMaybe>> - visibility?: InputMaybe -} - export type Stat = { commented?: Maybe ranking?: Maybe diff --git a/src/pages/profile/profileSettings.page.tsx b/src/pages/profile/profileSettings.page.tsx index 3b4c3f30..02326938 100644 --- a/src/pages/profile/profileSettings.page.tsx +++ b/src/pages/profile/profileSettings.page.tsx @@ -177,7 +177,7 @@ export const ProfileSettingsPage = () => { value={(value) => updateFormField('bio', value)} initialValue={form.bio} allowEnterKey={false} - maxLength={80} + maxLength={120} />

{t('About myself')}

diff --git a/src/styles/app.scss b/src/styles/app.scss index 45c9cfe8..5354ebf5 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -31,9 +31,11 @@ // names from figma --black-50: #f7f7f8; --black-100: #e9e9ee; + --black-300: #9fa1a7; --black-500: #141414; --black-400: #696969; --white-500: #fff; + --blue-500: #2638d9; } [data-editor-dark-mode='true'] {