Author page fixes

This commit is contained in:
kvakazyambra 2023-09-03 01:14:34 +03:00
parent 50baf5b60f
commit b71c6a7515
4 changed files with 256 additions and 199 deletions

View File

@ -14,10 +14,13 @@
} }
.authorDetails { .authorDetails {
align-items: baseline;
display: flex;
flex: 1; flex: 1;
@include media-breakpoint-up(sm) {
align-items: baseline;
display: flex;
}
&.authorDetailsShrinked { &.authorDetailsShrinked {
flex: 0 0 auto; flex: 0 0 auto;
} }
@ -62,7 +65,7 @@
.authorAbout { .authorAbout {
color: rgb(0 0 0 / 60%); color: rgb(0 0 0 / 60%);
font-size: 1.5rem; font-size: 1.4rem;
line-height: 1.4; line-height: 1.4;
word-break: break-word; word-break: break-word;
} }
@ -442,11 +445,19 @@
} }
} }
.subscribersContainer {
display: flex;
flex-wrap: wrap;
font-size: 1.4rem;
margin-top: 1em;
}
.subscribers { .subscribers {
align-items: center;
cursor: pointer; cursor: pointer;
display: inline-flex; display: inline-flex;
margin-right: 3rem;
vertical-align: top; vertical-align: top;
align-items: center;
.userpic { .userpic {
background: var(--background-color); background: var(--background-color);
@ -459,7 +470,12 @@
} }
} }
.listWrapper { .subscribersCounter {
overflow: auto; margin-left: -0.6rem;
max-height: 70vh; }
.listWrapper {
max-height: 70vh;
overflow: auto;
padding-right: 2rem;
} }

View File

@ -39,6 +39,7 @@ type AuthorCardProps = {
class?: string class?: string
followers?: Author[] followers?: Author[]
subscriptions?: Array<Author | Topic> subscriptions?: Array<Author | Topic>
showPublicationsCounter?: boolean
} }
function isAuthor(value: Author | Topic): value is Author { function isAuthor(value: Author | Topic): value is Author {
@ -115,128 +116,168 @@ export const AuthorCard = (props: AuthorCardProps) => {
}) })
return ( return (
<div <>
class={clsx(styles.author, props.class)}
classList={{
['row']: props.isAuthorPage,
[styles.authorPage]: props.isAuthorPage,
[styles.authorComments]: props.isComments,
[styles.authorsListItem]: props.isAuthorsList,
[styles.feedMode]: props.isFeedMode,
[styles.nowrapView]: props.isNowrap
}}
>
<Show
when={props.isAuthorPage}
fallback={
<Userpic
name={props.author.name}
userpic={props.author.userpic}
hasLink={props.hasLink}
isBig={props.isAuthorPage}
isAuthorsList={props.isAuthorsList}
isFeedMode={props.isFeedMode}
class={styles.circlewrap}
/>
}
>
<div class="col-md-5">
<Userpic
name={props.author.name}
userpic={props.author.userpic}
hasLink={props.hasLink}
isBig={props.isAuthorPage}
isAuthorsList={props.isAuthorsList}
isFeedMode={props.isFeedMode}
class={styles.circlewrap}
/>
</div>
</Show>
<div <div
class={styles.authorDetails} class={clsx(styles.author, props.class)}
classList={{ classList={{
'col-md-15 col-xl-13': props.isAuthorPage, ['row']: props.isAuthorPage,
[styles.authorDetailsShrinked]: props.isAuthorPage [styles.authorPage]: props.isAuthorPage,
[styles.authorComments]: props.isComments,
[styles.authorsListItem]: props.isAuthorsList,
[styles.feedMode]: props.isFeedMode,
[styles.nowrapView]: props.isNowrap
}} }}
> >
<div class={styles.authorDetailsWrapper}> <Show
<div class={styles.authorNameContainer}> when={props.isAuthorPage}
<ConditionalWrapper fallback={
condition={props.hasLink} <Userpic
wrapper={(children) => ( name={props.author.name}
<a class={styles.authorName} href={`/author/${props.author.slug}`}> userpic={props.author.userpic}
{children} hasLink={props.hasLink}
</a> isBig={props.isAuthorPage}
)} isAuthorsList={props.isAuthorsList}
> isFeedMode={props.isFeedMode}
<span class={clsx({ [styles.authorName]: !props.hasLink })}>{name()}</span> class={styles.circlewrap}
</ConditionalWrapper>
</div>
{/*TODO: implement plurals by i18n*/}
<Show when={props.author.bio} fallback={<div>{props.author.stat?.shouts} публикаций</div>}>
<div
class={styles.authorAbout}
classList={{ 'text-truncate': props.truncateBio }}
innerHTML={props.author.bio}
/> />
</Show> }
>
<div class="col-md-5">
<Userpic
name={props.author.name}
userpic={props.author.userpic}
hasLink={props.hasLink}
isBig={props.isAuthorPage}
isAuthorsList={props.isAuthorsList}
isFeedMode={props.isFeedMode}
class={styles.circlewrap}
/>
</div>
</Show>
<Show when={props.followers && props.followers.length > 0}> <div
<div class={styles.subscribers} onClick={() => showModal('followers')}> class={styles.authorDetails}
<For each={props.followers.slice(0, 3)}> classList={{
{(f) => <Userpic name={f.name} userpic={f.userpic} class={styles.userpic} />} 'col-md-15 col-xl-13': props.isAuthorPage,
</For> [styles.authorDetailsShrinked]: props.isAuthorPage
<div> }}
{props.followers.length}&nbsp; >
{getNumeralsDeclension(props.followers.length, [ <div class={styles.authorDetailsWrapper}>
t('subscriber'), <div class={styles.authorNameContainer}>
t('subscriber_rp'), <ConditionalWrapper
t('subscribers') condition={props.hasLink}
])} wrapper={(children) => (
</div> <a class={styles.authorName} href={`/author/${props.author.slug}`}>
{children}
</a>
)}
>
<span class={clsx({ [styles.authorName]: !props.hasLink })}>{name()}</span>
</ConditionalWrapper>
</div> </div>
</Show> {/*TODO: implement plurals by i18n*/}
<Show when={props.subscriptions && props.subscriptions.length > 0}> <Show
<div> when={props.author.bio}
<div class={styles.subscribers} onClick={() => showModal('subscriptions')}> fallback={
<For each={props.subscriptions.slice(0, 3)}> props.showPublicationsCounter ? (
{(f) => { <div class={styles.authorAbout}>{props.author.stat?.shouts} публикаций</div>
if ('name' in f) { ) : (
return <Userpic name={f.name} userpic={f.userpic} class={styles.userpic} /> ''
} else if ('title' in f) { )
return <Userpic name={f.title} userpic={f.pic} class={styles.userpic} /> }
} >
return null <div
}} class={styles.authorAbout}
</For> classList={{ 'text-truncate': props.truncateBio }}
<div> innerHTML={props.author.bio}
{props.subscriptions.length}&nbsp; />
{getNumeralsDeclension(props.subscriptions.length, [ </Show>
t('subscription'),
t('subscription_rp'), <Show
t('subscriptions') when={
])} (props.followers && props.followers.length > 0) ||
</div> (props.subscriptions && props.subscriptions.length > 0)
</div> }
</div> >
</Show> <div class={styles.subscribersContainer}>
</div> <Show when={props.followers && props.followers.length > 0}>
<ShowOnlyOnClient> <div class={styles.subscribers} onClick={() => showModal('followers')}>
<Show when={isSessionLoaded()}> <For each={props.followers.slice(0, 3)}>
<Show when={canFollow()}> {(f) => <Userpic name={f.name} userpic={f.userpic} class={styles.userpic} />}
<div class={styles.authorSubscribe}> </For>
<Show when={!props.noSocialButtons && !props.hideWriteButton}> <div class={styles.subscribersCounter}>
<div class={styles.authorSubscribeSocial}> {props.followers.length}&nbsp;
<For each={props.author.links}>{(link) => <a href={link} />}</For> {getNumeralsDeclension(props.followers.length, [
t('subscriber'),
t('subscriber_rp'),
t('subscribers')
])}
</div>
</div> </div>
</Show> </Show>
<Show when={props.subscriptions && props.subscriptions.length > 0}>
<div class={styles.subscribers} onClick={() => showModal('subscriptions')}>
<For each={props.subscriptions.slice(0, 3)}>
{(f) => {
if ('name' in f) {
return <Userpic name={f.name} userpic={f.userpic} class={styles.userpic} />
} else if ('title' in f) {
return <Userpic name={f.title} userpic={f.pic} class={styles.userpic} />
}
return null
}}
</For>
<div class={styles.subscribersCounter}>
{props.subscriptions.length}&nbsp;
{getNumeralsDeclension(props.subscriptions.length, [
t('subscription'),
t('subscription_rp'),
t('subscriptions')
])}
</div>
</div>
</Show>
</div>
</Show>
</div>
<ShowOnlyOnClient>
<Show when={isSessionLoaded()}>
<Show when={canFollow()}>
<div class={styles.authorSubscribe}>
<Show when={!props.noSocialButtons && !props.hideWriteButton}>
<div class={styles.authorSubscribeSocial}>
<For each={props.author.links}>{(link) => <a href={link} />}</For>
</div>
</Show>
<Show <Show
when={subscribed()} when={subscribed()}
fallback={ fallback={
<button
onClick={handleSubscribe}
class={clsx('button', styles.button)}
classList={{
[styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton,
'button--subscribe': !props.isAuthorsList,
'button--subscribe-topic': props.isAuthorsList || props.isTextButton,
[styles.buttonWrite]: props.isAuthorsList || props.isTextButton,
[styles.isSubscribing]: isSubscribing()
}}
disabled={isSubscribing()}
>
<Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}>
<Icon name="author-subscribe" class={styles.icon} />
</Show>
<Show when={props.isTextButton || props.isAuthorPage}>
<span class={clsx(styles.buttonLabel, styles.buttonLabelVisible)}>
{t('Follow')}
</span>
</Show>
</button>
}
>
<button <button
onClick={handleSubscribe} onClick={() => subscribe(false)}
class={clsx('button', styles.button)} class={clsx('button', styles.button)}
classList={{ classList={{
[styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton, [styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton,
@ -248,94 +289,82 @@ export const AuthorCard = (props: AuthorCardProps) => {
disabled={isSubscribing()} disabled={isSubscribing()}
> >
<Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}> <Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}>
<Icon name="author-subscribe" class={styles.icon} /> <Icon name="author-unsubscribe" class={styles.icon} />
</Show> </Show>
<Show when={props.isTextButton || props.isAuthorPage}> <Show when={props.isTextButton || props.isAuthorPage}>
<span class={clsx(styles.buttonLabel, styles.buttonLabelVisible)}> <span
{t('Follow')} class={clsx(
styles.buttonLabel,
styles.buttonLabelVisible,
styles.buttonUnfollowLabel
)}
>
{t('Unfollow')}
</span>
<span
class={clsx(
styles.buttonLabel,
styles.buttonLabelVisible,
styles.buttonSubscribedLabel
)}
>
{t('You are subscribed')}
</span> </span>
</Show> </Show>
</button> </button>
} </Show>
>
<button
onClick={() => subscribe(false)}
class={clsx('button', styles.button)}
classList={{
[styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton,
'button--subscribe': !props.isAuthorsList,
'button--subscribe-topic': props.isAuthorsList || props.isTextButton,
[styles.buttonWrite]: props.isAuthorsList || props.isTextButton,
[styles.isSubscribing]: isSubscribing()
}}
disabled={isSubscribing()}
>
<Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}>
<Icon name="author-unsubscribe" class={styles.icon} />
</Show>
<Show when={props.isTextButton || props.isAuthorPage}>
<span
class={clsx(
styles.buttonLabel,
styles.buttonLabelVisible,
styles.buttonUnfollowLabel
)}
>
{t('Unfollow')}
</span>
<span
class={clsx(
styles.buttonLabel,
styles.buttonLabelVisible,
styles.buttonSubscribedLabel
)}
>
{t('You are subscribed')}
</span>
</Show>
</button>
</Show>
<Show when={!props.hideWriteButton}> <Show when={!props.hideWriteButton}>
<button <button
class={styles.button} class={styles.button}
classList={{ classList={{
[styles.buttonSubscribe]: !props.isAuthorsList, [styles.buttonSubscribe]: !props.isAuthorsList,
'button--subscribe': !props.isAuthorsList, 'button--subscribe': !props.isAuthorsList,
'button--subscribe-topic': props.isAuthorsList, 'button--subscribe-topic': props.isAuthorsList,
[styles.buttonWrite]: props.liteButtons && props.isAuthorsList [styles.buttonWrite]: props.liteButtons && props.isAuthorsList
}} }}
onClick={initChat} onClick={initChat}
> >
<Show when={!props.isTextButton && !props.isAuthorPage}> <Show when={!props.isTextButton && !props.isAuthorPage}>
<Icon name="comment" class={styles.icon} /> <Icon name="comment" class={styles.icon} />
</Show> </Show>
<Show when={!props.liteButtons || props.isTextButton}>{t('Write')}</Show> <Show when={!props.liteButtons || props.isTextButton}>{t('Write')}</Show>
</button> </button>
</Show> </Show>
</div> </div>
</Show>
</Show> </Show>
</Show> </ShowOnlyOnClient>
</ShowOnlyOnClient> </div>
</div> </div>
<Show when={props.followers}> <Show when={props.followers}>
<Modal variant="wide" name="followers"> <Modal variant="wide" name="followers">
<> <>
<h2>{t('Followers')}</h2> <h2>{t('Followers')}</h2>
<div class={styles.listWrapper}> <div class={styles.listWrapper}>
<div class="row"> <div class="row">
<For each={props.followers}> <div class="col-24">
{(follower: Author) => ( <For each={props.followers}>
<div class="col-xs-12"> {(follower: Author) => (
<AuthorCard author={follower} hideWriteButton={true} hasLink={true} /> <AuthorCard
</div> author={follower}
)} hideWriteButton={true}
</For> hasLink={true}
isTextButton={true}
liteButtons={true}
truncateBio={true}
showPublicationsCounter={true}
/>
)}
</For>
</div>
</div> </div>
</div> </div>
</> </>
</Modal> </Modal>
</Show> </Show>
<Show when={props.subscriptions}> <Show when={props.subscriptions}>
<Modal variant="wide" name="subscriptions"> <Modal variant="wide" name="subscriptions">
<> <>
@ -357,29 +386,32 @@ export const AuthorCard = (props: AuthorCardProps) => {
</button> </button>
</li> </li>
</ul> </ul>
<br />
<div class={styles.listWrapper}> <div class={styles.listWrapper}>
<div class="row"> <div class="row">
<For each={subscriptions()}> <div class="col-24">
{(subscription: Author | Topic) => ( <For each={subscriptions()}>
<div class="col-xs-12"> {(subscription: Author | Topic) =>
{isAuthor(subscription) ? ( isAuthor(subscription) ? (
<AuthorCard <AuthorCard
author={subscription} author={subscription}
hideWriteButton={true} hideWriteButton={true}
hasLink={true} hasLink={true}
isTextButton={true} isTextButton={true}
truncateBio={true}
showPublicationsCounter={true}
/> />
) : ( ) : (
<TopicCard compact isTopicInRow showDescription topic={subscription} /> <TopicCard compact isTopicInRow showDescription isCardMode topic={subscription} />
)} )
</div> }
)} </For>
</For> </div>
</div> </div>
</div> </div>
</> </>
</Modal> </Modal>
</Show> </Show>
</div> </>
) )
} }

View File

@ -398,8 +398,10 @@
padding: 0 2.4rem; padding: 0 2.4rem;
width: 100%; width: 100%;
@include media-breakpoint-down(sm) { @include media-breakpoint-down(xl) {
padding-top: 100%; aspect-ratio: auto;
height: 100%;
padding-top: 30%;
} }
&.swiper-slide { &.swiper-slide {
@ -450,6 +452,12 @@
justify-content: end; justify-content: end;
padding: 2.4rem; padding: 2.4rem;
z-index: 1; z-index: 1;
@include media-breakpoint-down(xl) {
padding-left: 0;
padding-right: 0;
position: relative;
}
} }
.shoutCardCover { .shoutCardCover {

View File

@ -19,6 +19,7 @@
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-top: 0;
button { button {
margin-top: 0; margin-top: 0;