From bfe1ef2e85c3f10041b3f561e02961eb65cc5903 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:21:40 +0300 Subject: [PATCH] Feature/refactoring user card (#274) * Refactoring AuthorCard * fix alphabet sort --- src/components/Article/Article.module.scss | 1 - src/components/Article/Comment.tsx | 10 +- .../Article/CommentDate.module.scss | 8 +- src/components/Article/FullArticle.tsx | 5 +- .../Author/AhtorLink/AhtorLink.module.scss | 49 +++ .../Author/AhtorLink/AuthorLink.tsx | 21 ++ src/components/Author/AhtorLink/index.ts | 1 + .../AuthorBadge/AuthorBadge.module.scss | 20 +- .../Author/AuthorBadge/AuthorBadge.tsx | 49 ++- .../Author/AuthorCard/AuthorCard.module.scss | 351 ++++-------------- .../Author/AuthorCard/AuthorCard.tsx | 253 +++---------- .../Author/Userpic/Userpic.module.scss | 73 ++-- src/components/Author/Userpic/Userpic.tsx | 42 ++- .../{ => ArticleCard}/ArticleCard.module.scss | 2 + .../Feed/{ => ArticleCard}/ArticleCard.tsx | 42 +-- src/components/Feed/ArticleCard/index.tsx | 1 + src/components/Nav/HeaderAuth.tsx | 1 + src/components/Nav/ProfileModal.tsx | 50 --- .../NotificationView.module.scss | 3 +- .../NotificationView/NotificationView.tsx | 22 +- src/components/Views/AllAuthors.tsx | 49 ++- src/components/Views/Author/Author.tsx | 8 +- src/components/Views/Feed.module.scss | 4 + src/components/Views/Feed.tsx | 18 +- .../GroupAvatar/GroupAvatar.module.scss | 88 +++++ .../_shared/GroupAvatar/GroupAvatar.tsx | 43 +++ src/components/_shared/GroupAvatar/index.ts | 1 + .../_shared/VotersList/VotersList.tsx | 7 +- src/pages/profile/profileSettings.page.tsx | 2 +- 29 files changed, 529 insertions(+), 695 deletions(-) create mode 100644 src/components/Author/AhtorLink/AhtorLink.module.scss create mode 100644 src/components/Author/AhtorLink/AuthorLink.tsx create mode 100644 src/components/Author/AhtorLink/index.ts rename src/components/Feed/{ => ArticleCard}/ArticleCard.module.scss (99%) rename src/components/Feed/{ => ArticleCard}/ArticleCard.tsx (90%) create mode 100644 src/components/Feed/ArticleCard/index.tsx delete mode 100644 src/components/Nav/ProfileModal.tsx create mode 100644 src/components/_shared/GroupAvatar/GroupAvatar.module.scss create mode 100644 src/components/_shared/GroupAvatar/GroupAvatar.tsx create mode 100644 src/components/_shared/GroupAvatar/index.ts diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss index c9d72f1e..0d2a8b66 100644 --- a/src/components/Article/Article.module.scss +++ b/src/components/Article/Article.module.scss @@ -240,7 +240,6 @@ img { .shoutAuthorsList { border-bottom: 1px solid #e8e8e8; margin: 2em 0; - padding-bottom: 2em; h4 { color: #696969; diff --git a/src/components/Article/Comment.tsx b/src/components/Article/Comment.tsx index 3405b49b..dc723c6a 100644 --- a/src/components/Article/Comment.tsx +++ b/src/components/Article/Comment.tsx @@ -20,6 +20,7 @@ import { Author, Reaction, ReactionKind } from '../../graphql/types.gen' import { router } from '../../stores/router' import styles from './Comment.module.scss' +import { AuthorLink } from '../Author/AhtorLink' const SimplifiedEditor = lazy(() => import('../Editor/SimplifiedEditor')) @@ -135,7 +136,6 @@ export const Comment = (props: Props) => { { >
- +
diff --git a/src/components/Article/CommentDate.module.scss b/src/components/Article/CommentDate.module.scss index f9672066..0bc33f2d 100644 --- a/src/components/Article/CommentDate.module.scss +++ b/src/components/Article/CommentDate.module.scss @@ -1,14 +1,16 @@ .commentDates { - color: #9fa1a7; + @include font-size(1.2rem); + + color: var(--secondary-color); align-items: center; align-self: center; display: flex; flex: 1; flex-wrap: wrap; - @include font-size(1.2rem); font-size: 1.2rem; justify-content: flex-start; - margin: 0 1em 0 0; + margin: 0 1rem; + height: 1.6rem; .date { font-weight: 500; diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 3f26f74f..cb8e3b26 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -26,6 +26,7 @@ import { SolidSwiper } from '../_shared/SolidSwiper' import styles from './Article.module.scss' import { CardTopic } from '../Feed/CardTopic' import { createPopper } from '@popperjs/core' +import { AuthorBadge } from '../Author/AuthorBadge' type Props = { article: Shout @@ -437,9 +438,9 @@ export const FullArticle = (props: Props) => {

{t('Authors')}

- {(a) => ( + {(author) => (
- +
)}
diff --git a/src/components/Author/AhtorLink/AhtorLink.module.scss b/src/components/Author/AhtorLink/AhtorLink.module.scss new file mode 100644 index 00000000..ef7e911e --- /dev/null +++ b/src/components/Author/AhtorLink/AhtorLink.module.scss @@ -0,0 +1,49 @@ +.AuthorLink { + .link { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + gap: 1rem; + justify-content: center; + padding: 0; + border-bottom: none; + + &:hover { + background: unset !important; + border-bottom: none; + color: var(--default-color) !important; + } + + .name { + font-weight: 500; + line-height: 1; + margin-bottom: -2px; + + &:hover { + color: var(--default-color-invert); + background: var(--background-color-invert); + } + } + } + + // adjust size + &.XS { + .link { + gap: 0.5rem; + } + .name { + font-size: 1.2rem; + margin: 0; + } + } + + &.M { + .link { + gap: 1rem; + } + .name { + font-size: 1.4rem; + } + } +} diff --git a/src/components/Author/AhtorLink/AuthorLink.tsx b/src/components/Author/AhtorLink/AuthorLink.tsx new file mode 100644 index 00000000..ddc10cf4 --- /dev/null +++ b/src/components/Author/AhtorLink/AuthorLink.tsx @@ -0,0 +1,21 @@ +import { clsx } from 'clsx' +import styles from './AhtorLink.module.scss' +import { Author } from '../../../graphql/types.gen' +import { Userpic } from '../Userpic' + +type Props = { + author: Author + size?: 'XS' | 'M' | 'L' + class?: string +} + +export const AuthorLink = (props: Props) => { + return ( + + ) +} diff --git a/src/components/Author/AhtorLink/index.ts b/src/components/Author/AhtorLink/index.ts new file mode 100644 index 00000000..05f585a3 --- /dev/null +++ b/src/components/Author/AhtorLink/index.ts @@ -0,0 +1 @@ +export { AuthorLink } from './AuthorLink' diff --git a/src/components/Author/AuthorBadge/AuthorBadge.module.scss b/src/components/Author/AuthorBadge/AuthorBadge.module.scss index da653817..281ec4d0 100644 --- a/src/components/Author/AuthorBadge/AuthorBadge.module.scss +++ b/src/components/Author/AuthorBadge/AuthorBadge.module.scss @@ -3,6 +3,7 @@ display: flex; flex-flow: row nowrap; margin-bottom: 2rem; + gap: 1rem; @include media-breakpoint-down(sm) { flex-wrap: wrap; @@ -33,6 +34,11 @@ .name { color: var(--default-color); font-weight: 500; + + & span:hover { + color: var(--default-color-invert); + background: var(--background-color-invert); + } } .bio { @@ -42,7 +48,10 @@ .actions { flex: 0 20%; + display: flex; + flex-direction: row; margin-left: 5.2rem; + gap: 1rem; @include media-breakpoint-up(sm) { margin-left: 2rem; @@ -56,9 +65,18 @@ } } - .subscribeButton { + .actionButton { border-radius: 0.8rem !important; margin-right: 0 !important; width: 9em; + + &.iconed { + padding: 6px !important; + min-width: 32px; + width: unset; + &:hover img { + filter: invert(1); + } + } } } diff --git a/src/components/Author/AuthorBadge/AuthorBadge.tsx b/src/components/Author/AuthorBadge/AuthorBadge.tsx index d5f7bc42..26405ec5 100644 --- a/src/components/Author/AuthorBadge/AuthorBadge.tsx +++ b/src/components/Author/AuthorBadge/AuthorBadge.tsx @@ -8,10 +8,15 @@ import { Button } from '../../_shared/Button' import { useSession } from '../../../context/session' import { follow, unfollow } from '../../../stores/zine/common' import { CheckButton } from '../../_shared/CheckButton' +import { openPage } from '@nanostores/router' +import { router, useRouter } from '../../../stores/router' +import { Icon } from '../../_shared/Icon' type Props = { author: Author minimizeSubscribeButton?: boolean + showMessageButton?: boolean + iconButtons?: boolean } export const AuthorBadge = (props: Props) => { const [isSubscribing, setIsSubscribing] = createSignal(false) @@ -20,7 +25,7 @@ export const AuthorBadge = (props: Props) => { subscriptions, actions: { loadSubscriptions, requireAuthentication } } = useSession() - + const { changeSearchParam } = useRouter() const { t, formatDate } = useLocalize() const subscribed = createMemo(() => subscriptions().authors.some((author) => author.slug === props.author.slug) @@ -42,17 +47,34 @@ export const AuthorBadge = (props: Props) => { }, 'subscribe') } + const initChat = () => { + requireAuthentication(() => { + openPage(router, `inbox`) + changeSearchParam({ + initChat: props.author.id.toString() + }) + }, 'discussions') + } + const subscribeValue = createMemo(() => { + if (props.iconButtons) { + return + } + return isSubscribing() ? t('...subscribing') : t('Subscribe') + }) + return (
diff --git a/src/components/Author/AuthorCard/AuthorCard.module.scss b/src/components/Author/AuthorCard/AuthorCard.module.scss index 52b0d3d1..d51d1a66 100644 --- a/src/components/Author/AuthorCard/AuthorCard.module.scss +++ b/src/components/Author/AuthorCard/AuthorCard.module.scss @@ -1,6 +1,6 @@ .author { display: flex; - align-items: center; + align-items: flex-start; flex-flow: row nowrap; margin-bottom: 1.6rem; @@ -8,10 +8,47 @@ margin-bottom: 0; } + @include media-breakpoint-down(md) { + justify-content: center; + } @include media-breakpoint-up(md) { margin-bottom: 2.4rem; } + .authorName { + @include font-size(4rem); + font-weight: 700; + margin-bottom: 0.2em; + } + + .authorAbout { + color: #696969; + @include font-size(2rem); + font-weight: 500; + margin-top: 1.5rem; + } + + .authorActions { + margin: 2rem -0.8rem 0 0; + padding-left: 0; + display: flex; + flex-direction: row; + gap: 1rem; + + @include media-breakpoint-down(md) { + justify-content: center; + } + } + + .authorDetails { + display: block; + + @include media-breakpoint-down(md) { + flex: 1 100%; + text-align: center; + } + } + .listWrapper & { align-items: flex-start; margin-bottom: 2rem; @@ -39,7 +76,7 @@ } .authorDetails { - flex: 1; + flex: 0 0 auto; @include media-breakpoint-up(sm) { align-items: center; @@ -57,10 +94,6 @@ flex-wrap: nowrap; } } - - &.authorDetailsShrinked { - flex: 0 0 auto; - } } .authorDetailsWrapper { @@ -84,29 +117,9 @@ } } -.authorNameContainer { - line-height: 1.1; -} - .authorName { - border: none !important; - font-size: 1.6rem; - font-weight: 500; - margin-bottom: 0.8rem; - - .listWrapper & { - display: block; - - &:before { - content: ''; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 2; - } - } + @include font-size(4rem); + line-height: 1.1; } .authorAbout { @@ -117,42 +130,6 @@ word-break: break-word; } -.authorSubscribe { - align-items: center; - - @include media-breakpoint-down(md) { - flex-wrap: wrap; - } - - .button { - padding-left: 2rem; - padding-right: 2rem; - margin-right: 0.5em; - - &:first-of-type { - margin-left: 0; - } - - &:last-child { - margin-right: 0; - } - - &:hover { - .buttonUnfollowLabel { - display: block; - } - - .buttonSubscribedLabel { - display: none; - } - } - - .buttonUnfollowLabel { - display: none; - } - } -} - .authorSubscribeSocialLabel { display: none; } @@ -426,208 +403,6 @@ } } -.shareControl { - display: inline-block; -} - -.buttonSubscribe { - align-items: center; - aspect-ratio: 1/1; - border-radius: 100%; - display: inline-flex; - float: right; - - img { - display: block; - } -} - -.buttonLabel { - display: none; -} - -.buttonLabelVisible { - display: block; -} - -.buttonWrite { - background: #ccc; - color: #000; - display: inline-flex; - font-weight: 500; - transition: - background-color 0.3s, - color 0.3s; - - &:hover { - background: #000; - color: #fff; - - img { - filter: invert(1); - } - } - - .icon { - display: inline-block; - margin-right: 0.5em; - } - - img { - height: 15px; - transition: filter 0.3s; - } -} - -.authorPage { - align-items: center; - - @include media-breakpoint-down(md) { - justify-content: center; - } - - .authorName { - @include font-size(4rem); - font-weight: 700; - margin-bottom: 0.2em; - } - - .authorAbout { - color: #696969; - @include font-size(2rem); - font-weight: 500; - margin-top: 1.5rem; - } - - .authorSubscribe { - margin: 2rem -0.8rem 0 0; - padding-left: 0; - - @include media-breakpoint-down(md) { - justify-content: center; - } - } - - .authorDetails { - display: block; - - @include media-breakpoint-down(md) { - flex: 1 100%; - text-align: center; - } - } - - .buttonLabel { - display: block; - } - - .buttonSubscribe { - aspect-ratio: auto; - background-color: #000; - border-color: #000; - border-radius: 0.8rem; - color: #fff; - float: none; - padding-bottom: 0.6rem; - padding-top: 0.6rem; - width: 10em; - - .icon { - margin-right: 0.5em; - - img { - filter: invert(1); - } - } - - &:hover { - background: #fff; - color: #000; - - .icon img { - filter: invert(0); - } - } - } - - .buttonSubscribe img { - vertical-align: text-top; - } - - .button { - min-height: 4rem; - margin: 0 0.8rem 0 0; - vertical-align: middle; - - @include media-breakpoint-down(sm) { - margin-bottom: 0.5em; - } - } -} - -.authorsListItem { - margin-bottom: 1em !important; - - .authorName { - @include font-size(2.2rem); - - font-weight: bold; - } - - .authorSubscribe { - align-items: baseline; - - @include media-breakpoint-down(sm) { - padding: 1rem 0 0; - } - } - - .buttonLabel { - display: block; - } -} - -.nowrapView { - flex-wrap: nowrap; - align-items: center; - margin: 0; -} - -.authorComments { - .authorName { - @include font-size(1.2rem); - line-height: 1.2; - margin-bottom: 0; - } - - .circlewrap { - margin-top: -0.4em; - } -} - -.isSubscribing { - opacity: 0.5; -} - -.feedMode { - align-items: center; - margin-bottom: 0.4rem; - - .authorName, - .authorAbout { - @include font-size(1.2rem); - - margin-bottom: 0; - } - - .circlewrap { - height: 1.6rem; - margin-right: 0.4rem; - min-width: 1.6rem; - width: 1.6rem; - } -} - .subscribersContainer { display: flex; flex-wrap: wrap; @@ -648,33 +423,33 @@ vertical-align: top; border-bottom: unset !important; - &:last-child { - margin-right: 0; - } + .subscribersItem { + position: relative; - .userpic { - background: var(--background-color); - box-shadow: 0 0 0 2px var(--background-color); - height: 1.8rem; - min-width: 1.8rem; - max-width: 1.8rem; - vertical-align: top; - width: 1.8rem; - - &:not(:first-child) { - margin-left: -1.8rem; + &:nth-child(1) { + z-index: 2; } - - > * { - line-height: 1.8rem; - min-width: auto; + &:nth-child(2) { + z-index: 1; + } + &:not(:last-child) { + margin-right: -4px; + box-shadow: 0 0 0 1px var(--background-color); } } -} -.subscribersCounter { - font-weight: 500; - margin-left: -0.6rem; + .subscribersCounter { + font-weight: 500; + margin-left: 1rem; + } + + &:hover { + background: none !important; + + .subscribersCounter { + background: var(--background-color-invert); + } + } } .listWrapper { diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index f13082b2..206a8af2 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -1,6 +1,5 @@ import type { Author } from '../../../graphql/types.gen' import { Userpic } from '../Userpic' -import { Icon } from '../../_shared/Icon' import { createEffect, createMemo, createSignal, For, Show } from 'solid-js' import { translit } from '../../../utils/ru2en' import { follow, unfollow } from '../../../stores/zine/common' @@ -11,7 +10,6 @@ import { FollowingEntity, Topic } from '../../../graphql/types.gen' import { router, useRouter } from '../../../stores/router' import { openPage, redirectPage } from '@nanostores/router' import { useLocalize } from '../../../context/localize' -import { ConditionalWrapper } from '../../_shared/ConditionalWrapper' import { Modal } from '../../Nav/Modal' import { SubscriptionFilter } from '../../../pages/types' import { isAuthor } from '../../../utils/isAuthor' @@ -22,28 +20,9 @@ import { getShareUrl, SharePopup } from '../../Article/SharePopup' import styles from './AuthorCard.module.scss' type Props = { - caption?: string - hideWriteButton?: boolean - hideDescription?: boolean - hideFollow?: boolean - hasLink?: boolean - subscribed?: boolean author: Author - isAuthorPage?: boolean - noSocialButtons?: boolean - isAuthorsList?: boolean - truncateBio?: boolean - liteButtons?: boolean - isTextButton?: boolean - isComments?: boolean - isFeedMode?: boolean - isNowrap?: boolean - class?: string followers?: Author[] following?: Array - showPublicationsCounter?: boolean - hideBio?: boolean - isCurrentUser?: boolean } export const AuthorCard = (props: Props) => { @@ -58,7 +37,6 @@ export const AuthorCard = (props: Props) => { const [isSubscribing, setIsSubscribing] = createSignal(false) const [following, setFollowing] = createSignal>(props.following) const [subscriptionFilter, setSubscriptionFilter] = createSignal('all') - const [userpicUrl, setUserpicUrl] = createSignal() const subscribed = createMemo(() => subscriptions().authors.some((author) => author.slug === props.author.slug) @@ -75,7 +53,7 @@ export const AuthorCard = (props: Props) => { setIsSubscribing(false) } - const canFollow = createMemo(() => !props.hideFollow && session()?.user?.slug !== props.author.slug) + const isProfileOwner = createMemo(() => session()?.user?.slug === props.author.slug) const name = createMemo(() => { if (lang() !== 'ru') { @@ -102,7 +80,7 @@ export const AuthorCard = (props: Props) => { const handleSubscribe = () => { requireAuthentication(() => { - subscribe(true) + subscribe(!subscribed()) }, 'subscribe') } @@ -118,89 +96,28 @@ export const AuthorCard = (props: Props) => { } }) - if (props.isAuthorPage && props.author.userpic?.includes('assets.discours.io')) { - setUserpicUrl(props.author.userpic.replace('100x', '500x500')) + const followButtonText = () => { + if (isSubscribing()) { + return t('...subscribing') + } + return t(subscribed() ? 'Unfollow' : 'Follow') } + return ( -
- - } - > -
- -
-
- -
+
+
+ +
+
-
- ( - - {children} - - )} - > - {name()} - -
- - {t('PublicationsWithCount', { count: props.author.stat?.shouts ?? 0 })} -
- ) : ( - '' - ) - } - > -
- - +
{name()}
+
0) || @@ -211,10 +128,17 @@ export const AuthorCard = (props: Props) => { 0}> - {(f) => } + {(f) => ( + + )}
- {t('SubscriberWithCount', { count: props.followers.length })} + {t('SubscriberWithCount', { count: props.followers.length ?? 0 })}
@@ -224,9 +148,23 @@ export const AuthorCard = (props: Props) => { {(f) => { if ('name' in f) { - return + return ( + + ) } else if ('title' in f) { - return + return ( + + ) } return null }} @@ -259,102 +197,23 @@ export const AuthorCard = (props: Props) => {
- -
- - - - - - - {t('Follow')} - - - - } - > - - - - - -
-
- - -
+ +
+ } + > +