refactoring:following
This commit is contained in:
parent
a25d50d99b
commit
652d0b647a
|
@ -541,4 +541,4 @@
|
||||||
"You've reached a non-existed page": "You've reached a non-existed page",
|
"You've reached a non-existed page": "You've reached a non-existed page",
|
||||||
"Your email": "Your email",
|
"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"
|
"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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -568,4 +568,4 @@
|
||||||
"You've successfully logged out": "Вы успешно вышли из аккаунта",
|
"You've successfully logged out": "Вы успешно вышли из аккаунта",
|
||||||
"Your email": "Ваш email",
|
"Your email": "Ваш 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.": "Ваше имя появится на странице вашего профиля и как ваша подпись в публикациях, комментариях и откликах"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,17 @@ import { Author, FollowingEntity } from '../../../graphql/schema/core.gen'
|
||||||
import { router, useRouter } from '../../../stores/router'
|
import { router, useRouter } from '../../../stores/router'
|
||||||
import { translit } from '../../../utils/ru2en'
|
import { translit } from '../../../utils/ru2en'
|
||||||
import { isCyrillic } from '../../../utils/translate'
|
import { isCyrillic } from '../../../utils/translate'
|
||||||
import { BadgeSubscribeButton } from '../../_shared/BadgeSubscribeButton'
|
|
||||||
import { Button } from '../../_shared/Button'
|
import { Button } from '../../_shared/Button'
|
||||||
import { CheckButton } from '../../_shared/CheckButton'
|
import { CheckButton } from '../../_shared/CheckButton'
|
||||||
import { ConditionalWrapper } from '../../_shared/ConditionalWrapper'
|
import { ConditionalWrapper } from '../../_shared/ConditionalWrapper'
|
||||||
|
import { FollowingButton } from '../../_shared/FollowingButton'
|
||||||
import { Icon } from '../../_shared/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
import { Userpic } from '../Userpic'
|
import { Userpic } from '../Userpic'
|
||||||
import styles from './AuthorBadge.module.scss'
|
import styles from './AuthorBadge.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
author: Author
|
author: Author
|
||||||
minimizeSubscribeButton?: boolean
|
minimize?: boolean
|
||||||
showMessageButton?: boolean
|
showMessageButton?: boolean
|
||||||
iconButtons?: boolean
|
iconButtons?: boolean
|
||||||
nameOnly?: boolean
|
nameOnly?: boolean
|
||||||
|
@ -32,14 +32,14 @@ type Props = {
|
||||||
export const AuthorBadge = (props: Props) => {
|
export const AuthorBadge = (props: Props) => {
|
||||||
const { mediaMatches } = useMediaQuery()
|
const { mediaMatches } = useMediaQuery()
|
||||||
const { author, requireAuthentication } = useSession()
|
const { author, requireAuthentication } = useSession()
|
||||||
const { follow, unfollow, subscriptions, subscribeInAction } = useFollowing()
|
const { follow, unfollow, follows, following } = useFollowing()
|
||||||
const [isMobileView, setIsMobileView] = createSignal(false)
|
const [isMobileView, setIsMobileView] = createSignal(false)
|
||||||
const [isSubscribed, setIsSubscribed] = createSignal<boolean>()
|
const [isFollowed, setIsFollowed] = createSignal<boolean>()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!(subscriptions && props.author)) return
|
if (!(follows && props.author)) return
|
||||||
const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.author?.id)
|
const followed = follows?.authors?.some((authorEntity) => authorEntity.id === props.author?.id)
|
||||||
setIsSubscribed(subscribed)
|
setIsFollowed(followed)
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
@ -73,9 +73,9 @@ export const AuthorBadge = (props: Props) => {
|
||||||
|
|
||||||
const handleFollowClick = () => {
|
const handleFollowClick = () => {
|
||||||
requireAuthentication(async () => {
|
requireAuthentication(async () => {
|
||||||
const handle = isSubscribed() ? unfollow : follow
|
const handle = isFollowed() ? unfollow : follow
|
||||||
await handle(FollowingEntity.Author, props.author.slug)
|
await handle(FollowingEntity.Author, props.author.slug)
|
||||||
}, 'subscribe')
|
}, 'follow')
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -131,12 +131,10 @@ export const AuthorBadge = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
<Show when={props.author.slug !== author()?.slug && !props.nameOnly}>
|
<Show when={props.author.slug !== author()?.slug && !props.nameOnly}>
|
||||||
<div class={styles.actions}>
|
<div class={styles.actions}>
|
||||||
<BadgeSubscribeButton
|
<FollowingButton
|
||||||
action={() => handleFollowClick()}
|
action={() => handleFollowClick()}
|
||||||
isSubscribed={isSubscribed()}
|
isFollowed={isFollowed()}
|
||||||
actionMessageType={
|
actionMessageType={following()?.slug === props.author.slug ? following().type : undefined}
|
||||||
subscribeInAction()?.slug === props.author.slug ? subscribeInAction().type : undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<Show when={props.showMessageButton}>
|
<Show when={props.showMessageButton}>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useFollowing } from '../../../context/following'
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
import { useSession } from '../../../context/session'
|
import { useSession } from '../../../context/session'
|
||||||
import { FollowingEntity, Topic } from '../../../graphql/schema/core.gen'
|
import { FollowingEntity, Topic } from '../../../graphql/schema/core.gen'
|
||||||
import { SubscriptionFilter } from '../../../pages/types'
|
import { FollowsFilter } from '../../../pages/types'
|
||||||
import { router, useRouter } from '../../../stores/router'
|
import { router, useRouter } from '../../../stores/router'
|
||||||
import { isAuthor } from '../../../utils/isAuthor'
|
import { isAuthor } from '../../../utils/isAuthor'
|
||||||
import { translit } from '../../../utils/ru2en'
|
import { translit } from '../../../utils/ru2en'
|
||||||
|
@ -33,19 +33,19 @@ export const AuthorCard = (props: Props) => {
|
||||||
const { t, lang } = useLocalize()
|
const { t, lang } = useLocalize()
|
||||||
const { author, isSessionLoaded, requireAuthentication } = useSession()
|
const { author, isSessionLoaded, requireAuthentication } = useSession()
|
||||||
const [authorSubs, setAuthorSubs] = createSignal<Array<Author | Topic | Community>>([])
|
const [authorSubs, setAuthorSubs] = createSignal<Array<Author | Topic | Community>>([])
|
||||||
const [subscriptionFilter, setSubscriptionFilter] = createSignal<SubscriptionFilter>('all')
|
const [followsFilter, setFollowsFilter] = createSignal<FollowsFilter>('all')
|
||||||
const [isSubscribed, setIsSubscribed] = createSignal<boolean>()
|
const [isFollowed, setIsFollowed] = createSignal<boolean>()
|
||||||
const isProfileOwner = createMemo(() => author()?.slug === props.author.slug)
|
const isProfileOwner = createMemo(() => author()?.slug === props.author.slug)
|
||||||
const { follow, unfollow, subscriptions, subscribeInAction } = useFollowing()
|
const { follow, unfollow, follows, following } = useFollowing()
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
setAuthorSubs(props.following)
|
setAuthorSubs(props.following)
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (!(subscriptions && props.author)) return
|
if (!(follows && props.author)) return
|
||||||
const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.author?.id)
|
const followed = follows?.authors?.some((authorEntity) => authorEntity.id === props.author?.id)
|
||||||
setIsSubscribed(subscribed)
|
setIsFollowed(followed)
|
||||||
})
|
})
|
||||||
|
|
||||||
const name = createMemo(() => {
|
const name = createMemo(() => {
|
||||||
|
@ -72,11 +72,11 @@ export const AuthorCard = (props: Props) => {
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (props.following) {
|
if (props.following) {
|
||||||
if (subscriptionFilter() === 'authors') {
|
if (followsFilter() === 'authors') {
|
||||||
setAuthorSubs(props.following.filter((s) => 'name' in s))
|
setAuthorSubs(props.following.filter((s) => 'name' in s))
|
||||||
} else if (subscriptionFilter() === 'topics') {
|
} else if (followsFilter() === 'topics') {
|
||||||
setAuthorSubs(props.following.filter((s) => 'title' in s))
|
setAuthorSubs(props.following.filter((s) => 'title' in s))
|
||||||
} else if (subscriptionFilter() === 'communities') {
|
} else if (followsFilter() === 'communities') {
|
||||||
setAuthorSubs(props.following.filter((s) => 'title' in s))
|
setAuthorSubs(props.following.filter((s) => 'title' in s))
|
||||||
} else {
|
} else {
|
||||||
setAuthorSubs(props.following)
|
setAuthorSubs(props.following)
|
||||||
|
@ -86,18 +86,18 @@ export const AuthorCard = (props: Props) => {
|
||||||
|
|
||||||
const handleFollowClick = () => {
|
const handleFollowClick = () => {
|
||||||
requireAuthentication(() => {
|
requireAuthentication(() => {
|
||||||
isSubscribed()
|
isFollowed()
|
||||||
? unfollow(FollowingEntity.Author, props.author.slug)
|
? unfollow(FollowingEntity.Author, props.author.slug)
|
||||||
: follow(FollowingEntity.Author, props.author.slug)
|
: follow(FollowingEntity.Author, props.author.slug)
|
||||||
}, 'subscribe')
|
}, 'follow')
|
||||||
}
|
}
|
||||||
|
|
||||||
const followButtonText = createMemo(() => {
|
const followButtonText = createMemo(() => {
|
||||||
if (subscribeInAction()?.slug === props.author.slug) {
|
if (following()?.slug === props.author.slug) {
|
||||||
return subscribeInAction().type === 'subscribe' ? t('Subscribing...') : t('Unsubscribing...')
|
return following().type === 'follow' ? t('Following...') : t('Unfollowing...')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSubscribed()) {
|
if (isFollowed()) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span class={stylesButton.buttonSubscribeLabel}>{t('Following')}</span>
|
<span class={stylesButton.buttonSubscribeLabel}>{t('Following')}</span>
|
||||||
|
@ -208,11 +208,11 @@ export const AuthorCard = (props: Props) => {
|
||||||
<Show when={authorSubs()?.length}>
|
<Show when={authorSubs()?.length}>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleFollowClick}
|
onClick={handleFollowClick}
|
||||||
disabled={Boolean(subscribeInAction())}
|
disabled={Boolean(following())}
|
||||||
value={followButtonText()}
|
value={followButtonText()}
|
||||||
isSubscribeButton={true}
|
isSubscribeButton={true}
|
||||||
class={clsx({
|
class={clsx({
|
||||||
[stylesButton.subscribed]: isSubscribed(),
|
[stylesButton.followed]: isFollowed(),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -272,20 +272,20 @@ export const AuthorCard = (props: Props) => {
|
||||||
<ul class="view-switcher">
|
<ul class="view-switcher">
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
'view-switcher__item--selected': subscriptionFilter() === 'all',
|
'view-switcher__item--selected': followsFilter() === 'all',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button type="button" onClick={() => setSubscriptionFilter('all')}>
|
<button type="button" onClick={() => setFollowsFilter('all')}>
|
||||||
{t('All')}
|
{t('All')}
|
||||||
</button>
|
</button>
|
||||||
<span class="view-switcher__counter">{props.following.length}</span>
|
<span class="view-switcher__counter">{props.following.length}</span>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
'view-switcher__item--selected': subscriptionFilter() === 'authors',
|
'view-switcher__item--selected': followsFilter() === 'authors',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button type="button" onClick={() => setSubscriptionFilter('authors')}>
|
<button type="button" onClick={() => setFollowsFilter('authors')}>
|
||||||
{t('Authors')}
|
{t('Authors')}
|
||||||
</button>
|
</button>
|
||||||
<span class="view-switcher__counter">
|
<span class="view-switcher__counter">
|
||||||
|
@ -294,10 +294,10 @@ export const AuthorCard = (props: Props) => {
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
'view-switcher__item--selected': subscriptionFilter() === 'topics',
|
'view-switcher__item--selected': followsFilter() === 'topics',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button type="button" onClick={() => setSubscriptionFilter('topics')}>
|
<button type="button" onClick={() => setFollowsFilter('topics')}>
|
||||||
{t('Topics')}
|
{t('Topics')}
|
||||||
</button>
|
</button>
|
||||||
<span class="view-switcher__counter">
|
<span class="view-switcher__counter">
|
||||||
|
|
|
@ -30,7 +30,7 @@ export default Node.create({
|
||||||
addOptions() {
|
addOptions() {
|
||||||
return {
|
return {
|
||||||
'data-type': 'incut',
|
'data-type': 'incut',
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
|
@ -48,20 +48,20 @@ export default Node.create({
|
||||||
return {
|
return {
|
||||||
toggleArticle:
|
toggleArticle:
|
||||||
() =>
|
() =>
|
||||||
// eslint-disable-next-line unicorn/consistent-function-scoping
|
// eslint-disable-next-line unicorn/consistent-function-scoping
|
||||||
({ commands }) => {
|
({ commands }) => {
|
||||||
return commands.toggleWrap('article')
|
return commands.toggleWrap('article')
|
||||||
},
|
},
|
||||||
setArticleFloat:
|
setArticleFloat:
|
||||||
(value) =>
|
(value) =>
|
||||||
({ commands }) => {
|
({ commands }) => {
|
||||||
return commands.updateAttributes(this.name, { 'data-float': value })
|
return commands.updateAttributes(this.name, { 'data-float': value })
|
||||||
},
|
},
|
||||||
setArticleBg:
|
setArticleBg:
|
||||||
(value) =>
|
(value) =>
|
||||||
({ commands }) => {
|
({ commands }) => {
|
||||||
return commands.updateAttributes(this.name, { 'data-bg': value })
|
return commands.updateAttributes(this.name, { 'data-bg': value })
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const CustomBlockquote = Blockquote.extend({
|
||||||
content: 'block+',
|
content: 'block+',
|
||||||
|
|
||||||
addOptions(): BlockquoteOptions {
|
addOptions(): BlockquoteOptions {
|
||||||
return {} as BlockquoteOptions;
|
return {} as BlockquoteOptions
|
||||||
},
|
},
|
||||||
|
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
|
@ -35,15 +35,13 @@ export const CustomBlockquote = Blockquote.extend({
|
||||||
addCommands() {
|
addCommands() {
|
||||||
return {
|
return {
|
||||||
toggleBlockquote:
|
toggleBlockquote:
|
||||||
(type) => ({ commands }) => commands.toggleWrap(
|
(type) =>
|
||||||
this.name,
|
({ commands }) =>
|
||||||
{ 'data-type': type }
|
commands.toggleWrap(this.name, { 'data-type': type }),
|
||||||
),
|
|
||||||
setBlockQuoteFloat:
|
setBlockQuoteFloat:
|
||||||
(value) => ({ commands }) => commands.updateAttributes(
|
(value) =>
|
||||||
this.name,
|
({ commands }) =>
|
||||||
{ 'data-float': value }
|
commands.updateAttributes(this.name, { 'data-float': value }),
|
||||||
),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,7 +15,7 @@ import styles from './Sidebar.module.scss'
|
||||||
export const Sidebar = () => {
|
export const Sidebar = () => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { seen } = useSeen()
|
const { seen } = useSeen()
|
||||||
const { subscriptions } = useFollowing()
|
const { follows } = useFollowing()
|
||||||
const { page } = useRouter()
|
const { page } = useRouter()
|
||||||
const { articlesByTopic, articlesByAuthor } = useArticlesStore()
|
const { articlesByTopic, articlesByAuthor } = useArticlesStore()
|
||||||
const [isSubscriptionsVisible, setSubscriptionsVisible] = createSignal(true)
|
const [isSubscriptionsVisible, setSubscriptionsVisible] = createSignal(true)
|
||||||
|
@ -111,7 +111,7 @@ export const Sidebar = () => {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<Show when={subscriptions.authors.length > 0 || subscriptions.topics.length > 0}>
|
<Show when={follows?.authors?.length > 0 || follows?.topics?.length > 0}>
|
||||||
<h4
|
<h4
|
||||||
classList={{ [styles.opened]: isSubscriptionsVisible() }}
|
classList={{ [styles.opened]: isSubscriptionsVisible() }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -123,7 +123,7 @@ export const Sidebar = () => {
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<ul class={clsx(styles.subscriptions, { [styles.hidden]: !isSubscriptionsVisible() })}>
|
<ul class={clsx(styles.subscriptions, { [styles.hidden]: !isSubscriptionsVisible() })}>
|
||||||
<For each={subscriptions.authors}>
|
<For each={follows.authors}>
|
||||||
{(a: Author) => (
|
{(a: Author) => (
|
||||||
<li>
|
<li>
|
||||||
<a href={`/author/${a.slug}`} classList={{ [styles.unread]: checkAuthorIsSeen(a.slug) }}>
|
<a href={`/author/${a.slug}`} classList={{ [styles.unread]: checkAuthorIsSeen(a.slug) }}>
|
||||||
|
@ -135,7 +135,7 @@ export const Sidebar = () => {
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
<For each={subscriptions.topics}>
|
<For each={follows.topics}>
|
||||||
{(topic) => (
|
{(topic) => (
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -123,12 +123,12 @@
|
||||||
width: 9em;
|
width: 9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.isSubscribing {
|
.isFollowing {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.isSubscribed {
|
.isFollowed {
|
||||||
background: #000;
|
background: #000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
transition:
|
transition:
|
||||||
|
@ -158,4 +158,4 @@
|
||||||
|
|
||||||
.cardMode {
|
.cardMode {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { Show, createEffect, createMemo, createSignal } from 'solid-js'
|
import { Show, createEffect, createMemo, createSignal, on } from 'solid-js'
|
||||||
|
|
||||||
import { useFollowing } from '../../context/following'
|
import { useFollowing } from '../../context/following'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
|
@ -18,7 +18,7 @@ import styles from './Card.module.scss'
|
||||||
interface TopicProps {
|
interface TopicProps {
|
||||||
topic: Topic
|
topic: Topic
|
||||||
compact?: boolean
|
compact?: boolean
|
||||||
subscribed?: boolean
|
followed?: boolean
|
||||||
shortDescription?: boolean
|
shortDescription?: boolean
|
||||||
subscribeButtonBottom?: boolean
|
subscribeButtonBottom?: boolean
|
||||||
additionalClass?: string
|
additionalClass?: string
|
||||||
|
@ -27,7 +27,7 @@ interface TopicProps {
|
||||||
showPublications?: boolean
|
showPublications?: boolean
|
||||||
showDescription?: boolean
|
showDescription?: boolean
|
||||||
isCardMode?: boolean
|
isCardMode?: boolean
|
||||||
minimizeSubscribeButton?: boolean
|
minimize?: boolean
|
||||||
isNarrow?: boolean
|
isNarrow?: boolean
|
||||||
withIcon?: boolean
|
withIcon?: boolean
|
||||||
}
|
}
|
||||||
|
@ -38,33 +38,40 @@ export const TopicCard = (props: TopicProps) => {
|
||||||
capitalize(lang() === 'en' ? props.topic.slug.replaceAll('-', ' ') : props.topic.title || ''),
|
capitalize(lang() === 'en' ? props.topic.slug.replaceAll('-', ' ') : props.topic.title || ''),
|
||||||
)
|
)
|
||||||
const { author, requireAuthentication } = useSession()
|
const { author, requireAuthentication } = useSession()
|
||||||
const [isSubscribed, setIsSubscribed] = createSignal()
|
const [isFollowed, setIsFollowed] = createSignal()
|
||||||
const { follow, unfollow, subscriptions, subscribeInAction } = useFollowing()
|
const { follow, unfollow, follows, following } = useFollowing()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(
|
||||||
if (!(subscriptions && props.topic)) return
|
on(
|
||||||
const subscribed = subscriptions.topics?.some((topics) => topics.id === props.topic?.id)
|
[() => follows, () => props.topic],
|
||||||
setIsSubscribed(subscribed)
|
([flws, tpc]) => {
|
||||||
})
|
if (flws && tpc) {
|
||||||
|
const followed = flws.topics?.some((topics) => topics.id === props.topic?.id)
|
||||||
|
setIsFollowed(followed)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ defer: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const handleFollowClick = () => {
|
const handleFollowClick = () => {
|
||||||
requireAuthentication(() => {
|
requireAuthentication(() => {
|
||||||
isSubscribed()
|
isFollowed()
|
||||||
? unfollow(FollowingEntity.Topic, props.topic.slug)
|
? unfollow(FollowingEntity.Topic, props.topic.slug)
|
||||||
: follow(FollowingEntity.Topic, props.topic.slug)
|
: follow(FollowingEntity.Topic, props.topic.slug)
|
||||||
}, 'subscribe')
|
}, 'follow')
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscribeValue = () => {
|
const subscribeValue = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Show when={props.iconButton}>
|
<Show when={props.iconButton}>
|
||||||
<Show when={isSubscribed()} fallback="+">
|
<Show when={isFollowed()} fallback="+">
|
||||||
<Icon name="check-subscribed" />
|
<Icon name="check-followed" />
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={!props.iconButton}>
|
<Show when={!props.iconButton}>
|
||||||
<Show when={isSubscribed()} fallback={t('Follow')}>
|
<Show when={isFollowed()} fallback={t('Follow')}>
|
||||||
<span class={stylesButton.buttonSubscribeLabelHovered}>{t('Unfollow')}</span>
|
<span class={stylesButton.buttonSubscribeLabelHovered}>{t('Unfollow')}</span>
|
||||||
<span class={stylesButton.buttonSubscribeLabel}>{t('Following')}</span>
|
<span class={stylesButton.buttonSubscribeLabel}>{t('Following')}</span>
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -132,11 +139,11 @@ export const TopicCard = (props: TopicProps) => {
|
||||||
<ShowOnlyOnClient>
|
<ShowOnlyOnClient>
|
||||||
<Show when={author()}>
|
<Show when={author()}>
|
||||||
<Show
|
<Show
|
||||||
when={!props.minimizeSubscribeButton}
|
when={!props.minimize}
|
||||||
fallback={
|
fallback={
|
||||||
<CheckButton
|
<CheckButton
|
||||||
text={t('Follow')}
|
text={t('Follow')}
|
||||||
checked={Boolean(isSubscribed())}
|
checked={Boolean(isFollowed())}
|
||||||
onClick={handleFollowClick}
|
onClick={handleFollowClick}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -148,9 +155,9 @@ export const TopicCard = (props: TopicProps) => {
|
||||||
onClick={handleFollowClick}
|
onClick={handleFollowClick}
|
||||||
isSubscribeButton={true}
|
isSubscribeButton={true}
|
||||||
class={clsx(styles.actionButton, {
|
class={clsx(styles.actionButton, {
|
||||||
[styles.isSubscribing]:
|
[styles.isFollowing]:
|
||||||
subscribeInAction()?.slug === props.topic.slug ? subscribeInAction().type : undefined,
|
following()?.slug === props.topic.slug ? following().type : undefined,
|
||||||
[stylesButton.subscribed]: isSubscribed(),
|
[stylesButton.followed]: isFollowed(),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
|
@ -17,14 +17,13 @@ type Props = {
|
||||||
|
|
||||||
export const FullTopic = (props: Props) => {
|
export const FullTopic = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { subscriptions, setFollowing } = useFollowing()
|
const { follows, changeFollowing } = useFollowing()
|
||||||
const { requireAuthentication } = useSession()
|
const { requireAuthentication } = useSession()
|
||||||
const [followed, setFollowed] = createSignal()
|
const [followed, setFollowed] = createSignal()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const subs = subscriptions
|
if (follows?.topics.length !== 0) {
|
||||||
if (subs?.topics.length !== 0) {
|
const items = follows.topics || []
|
||||||
const items = subs.topics || []
|
|
||||||
setFollowed(items.some((x: Topic) => x?.slug === props.topic?.slug))
|
setFollowed(items.some((x: Topic) => x?.slug === props.topic?.slug))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -33,7 +32,7 @@ export const FullTopic = (props: Props) => {
|
||||||
const really = !followed()
|
const really = !followed()
|
||||||
setFollowed(really)
|
setFollowed(really)
|
||||||
requireAuthentication(() => {
|
requireAuthentication(() => {
|
||||||
setFollowing(FollowingEntity.Topic, props.topic.slug, really)
|
changeFollowing(FollowingEntity.Topic, props.topic.slug, really)
|
||||||
}, 'follow')
|
}, 'follow')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
@include font-size(1.4rem);
|
@include font-size(1.4rem);
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
// display: flex;
|
// display: flex;
|
||||||
|
@ -62,11 +63,13 @@
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
@include font-size(2.2rem);
|
@include font-size(2.2rem);
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
@include font-size(1.6rem);
|
@include font-size(1.6rem);
|
||||||
|
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
margin: 0.8rem 0;
|
margin: 0.8rem 0;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
|
@ -104,6 +107,7 @@
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
@include font-size(1.4rem);
|
@include font-size(1.4rem);
|
||||||
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
color: var(--blue-500);
|
color: var(--blue-500);
|
||||||
|
@ -111,8 +115,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
color: var(--black-400);
|
|
||||||
@include font-size(1.2rem);
|
@include font-size(1.2rem);
|
||||||
|
|
||||||
|
color: var(--black-400);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -160,4 +165,4 @@
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { Show, createEffect, createSignal } from 'solid-js'
|
import { Show, createEffect, createSignal, on } from 'solid-js'
|
||||||
|
|
||||||
import { useFollowing } from '../../../context/following'
|
import { useFollowing } from '../../../context/following'
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
@ -8,12 +8,12 @@ import { useSession } from '../../../context/session'
|
||||||
import { FollowingEntity, Topic } from '../../../graphql/schema/core.gen'
|
import { FollowingEntity, Topic } from '../../../graphql/schema/core.gen'
|
||||||
import { capitalize } from '../../../utils/capitalize'
|
import { capitalize } from '../../../utils/capitalize'
|
||||||
import { getImageUrl } from '../../../utils/getImageUrl'
|
import { getImageUrl } from '../../../utils/getImageUrl'
|
||||||
import { BadgeSubscribeButton } from '../../_shared/BadgeSubscribeButton'
|
import { FollowingButton } from '../../_shared/FollowingButton'
|
||||||
import styles from './TopicBadge.module.scss'
|
import styles from './TopicBadge.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
topic: Topic
|
topic: Topic
|
||||||
minimizeSubscribeButton?: boolean
|
minimize?: boolean
|
||||||
showStat?: boolean
|
showStat?: boolean
|
||||||
subscriptionsMode?: boolean
|
subscriptionsMode?: boolean
|
||||||
}
|
}
|
||||||
|
@ -23,18 +23,25 @@ export const TopicBadge = (props: Props) => {
|
||||||
const { mediaMatches } = useMediaQuery()
|
const { mediaMatches } = useMediaQuery()
|
||||||
const [isMobileView, setIsMobileView] = createSignal(false)
|
const [isMobileView, setIsMobileView] = createSignal(false)
|
||||||
const { requireAuthentication } = useSession()
|
const { requireAuthentication } = useSession()
|
||||||
const [isSubscribed, setIsSubscribed] = createSignal<boolean>()
|
const [isFollowed, setIsFollowed] = createSignal<boolean>()
|
||||||
const { follow, unfollow, subscriptions, subscribeInAction } = useFollowing()
|
const { follow, unfollow, follows, following } = useFollowing()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(
|
||||||
if (!(subscriptions && props.topic)) return
|
on(
|
||||||
const subscribed = subscriptions.topics?.some((topics) => topics.id === props.topic?.id)
|
[() => follows, () => props.topic],
|
||||||
setIsSubscribed(subscribed)
|
([flws, tpc]) => {
|
||||||
})
|
if (flws && tpc) {
|
||||||
|
const followed = follows?.topics?.some((topics) => topics.id === props.topic?.id)
|
||||||
|
setIsFollowed(followed)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ defer: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const handleFollowClick = () => {
|
const handleFollowClick = () => {
|
||||||
requireAuthentication(() => {
|
requireAuthentication(() => {
|
||||||
isSubscribed()
|
isFollowed()
|
||||||
? follow(FollowingEntity.Topic, props.topic.slug)
|
? follow(FollowingEntity.Topic, props.topic.slug)
|
||||||
: unfollow(FollowingEntity.Topic, props.topic.slug)
|
: unfollow(FollowingEntity.Topic, props.topic.slug)
|
||||||
}, 'subscribe')
|
}, 'subscribe')
|
||||||
|
@ -82,12 +89,10 @@ export const TopicBadge = (props: Props) => {
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class={styles.actions}>
|
<div class={styles.actions}>
|
||||||
<BadgeSubscribeButton
|
<FollowingButton
|
||||||
isSubscribed={isSubscribed()}
|
isFollowed={isFollowed()}
|
||||||
action={handleFollowClick}
|
action={handleFollowClick}
|
||||||
actionMessageType={
|
actionMessageType={following()?.slug === props.topic.slug ? following().type : undefined}
|
||||||
subscribeInAction()?.slug === props.topic.slug ? subscribeInAction().type : undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,54 +39,55 @@ const LOAD_MORE_PAGE_SIZE = 9
|
||||||
|
|
||||||
export const AuthorView = (props: Props) => {
|
export const AuthorView = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { followers: myFollowers } = useFollowing()
|
const { followers: myFollowers, follows: myFollows } = useFollowing()
|
||||||
const { session } = useSession()
|
const { session, author: me } = useSession()
|
||||||
const { sortedArticles } = useArticlesStore({ shouts: props.shouts })
|
const { sortedArticles } = useArticlesStore({ shouts: props.shouts })
|
||||||
const { page: getPage, searchParams } = useRouter()
|
const { page: getPage, searchParams } = useRouter()
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
const [isBioExpanded, setIsBioExpanded] = createSignal(false)
|
const [isBioExpanded, setIsBioExpanded] = createSignal(false)
|
||||||
const [author, setAuthor] = createSignal<Author>()
|
const [author, setAuthor] = createSignal<Author>()
|
||||||
const [followers, setFollowers] = createSignal([])
|
const [followers, setFollowers] = createSignal([])
|
||||||
const [following, setFollowing] = createSignal<Array<Author | Topic>>([]) // flat AuthorFollowsResult
|
const [following, changeFollowing] = createSignal<Array<Author | Topic>>([]) // flat AuthorFollowsResult
|
||||||
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
|
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
|
||||||
const [commented, setCommented] = createSignal<Reaction[]>()
|
const [commented, setCommented] = createSignal<Reaction[]>()
|
||||||
const modal = MODALS[searchParams().m]
|
const modal = MODALS[searchParams().m]
|
||||||
|
|
||||||
const [sessionChecked, setSessionChecked] = createSignal(false)
|
const [sessionChecked, setSessionChecked] = createSignal(false)
|
||||||
createEffect(() => {
|
createEffect(
|
||||||
if (
|
on(
|
||||||
!sessionChecked() &&
|
[() => sessionChecked(), () => props.authorSlug, () => session()?.user?.app_data?.profile?.slug],
|
||||||
props.authorSlug &&
|
([checked, slug, mySlug]) => {
|
||||||
session()?.user?.app_data?.profile?.slug === props.authorSlug
|
if (!checked && slug && mySlug === slug) {
|
||||||
) {
|
setSessionChecked(true)
|
||||||
setSessionChecked(true)
|
const appdata = session()?.user.app_data
|
||||||
const appdata = session()?.user.app_data
|
if (appdata) {
|
||||||
if (appdata) {
|
console.info('preloaded my own profile')
|
||||||
console.info('preloaded my own profile')
|
setFollowers(myFollowers())
|
||||||
const { authors, profile, topics } = appdata
|
setAuthor(me())
|
||||||
setFollowers(myFollowers)
|
const { authors, topics } = myFollows
|
||||||
setAuthor(profile)
|
changeFollowing([...authors, ...topics])
|
||||||
setFollowing([...authors, ...topics])
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
{ defer: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const bioContainerRef: { current: HTMLDivElement } = { current: null }
|
const bioContainerRef: { current: HTMLDivElement } = { current: null }
|
||||||
const bioWrapperRef: { current: HTMLDivElement } = { current: null }
|
const bioWrapperRef: { current: HTMLDivElement } = { current: null }
|
||||||
|
|
||||||
const fetchData = async (slug: string) => {
|
const fetchData = async (slug: string) => {
|
||||||
try {
|
try {
|
||||||
const [subscriptionsResult, followersResult, authorResult] = await Promise.all([
|
const [followsResult, followersResult, authorResult] = await Promise.all([
|
||||||
apiClient.getAuthorFollows({ slug }),
|
apiClient.getAuthorFollows({ slug }),
|
||||||
apiClient.getAuthorFollowers({ slug }),
|
apiClient.getAuthorFollowers({ slug }),
|
||||||
loadAuthor({ slug }),
|
loadAuthor({ slug }),
|
||||||
])
|
])
|
||||||
const { authors, topics } = subscriptionsResult
|
|
||||||
setAuthor(authorResult)
|
|
||||||
setFollowing([...(authors || []), ...(topics || [])])
|
|
||||||
setFollowers(followersResult || [])
|
|
||||||
|
|
||||||
console.info('[components.Author] data loaded')
|
console.info('[components.Author] data loaded')
|
||||||
|
setAuthor(authorResult)
|
||||||
|
setFollowers(followersResult || [])
|
||||||
|
const { authors, topics } = followsResult
|
||||||
|
changeFollowing([...(authors || []), ...(topics || [])])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[components.Author] fetch error', error)
|
console.error('[components.Author] fetch error', error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { For, Show, createEffect, createSignal } from 'solid-js'
|
import { For, Show, createEffect, createSignal, on } from 'solid-js'
|
||||||
|
|
||||||
import { useFollowing } from '../../../context/following'
|
import { useFollowing } from '../../../context/following'
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
import { Author, Topic } from '../../../graphql/schema/core.gen'
|
import { Author, Topic } from '../../../graphql/schema/core.gen'
|
||||||
import { SubscriptionFilter } from '../../../pages/types'
|
import { FollowsFilter } from '../../../pages/types'
|
||||||
import { dummyFilter } from '../../../utils/dummyFilter'
|
import { dummyFilter } from '../../../utils/dummyFilter'
|
||||||
// TODO: refactor styles
|
|
||||||
import { isAuthor } from '../../../utils/isAuthor'
|
import { isAuthor } from '../../../utils/isAuthor'
|
||||||
import { AuthorBadge } from '../../Author/AuthorBadge'
|
import { AuthorBadge } from '../../Author/AuthorBadge'
|
||||||
import { ProfileSettingsNavigation } from '../../Nav/ProfileSettingsNavigation'
|
import { ProfileSettingsNavigation } from '../../Nav/ProfileSettingsNavigation'
|
||||||
|
@ -19,30 +18,30 @@ import stylesSettings from '../../../styles/FeedSettings.module.scss'
|
||||||
|
|
||||||
export const ProfileSubscriptions = () => {
|
export const ProfileSubscriptions = () => {
|
||||||
const { t, lang } = useLocalize()
|
const { t, lang } = useLocalize()
|
||||||
const { subscriptions } = useFollowing()
|
const { follows } = useFollowing()
|
||||||
const [following, setFollowing] = createSignal<Array<Author | Topic>>([])
|
const [flatFollows, setFlatFollows] = createSignal<Array<Author | Topic>>([])
|
||||||
const [filtered, setFiltered] = createSignal<Array<Author | Topic>>([])
|
const [filtered, setFiltered] = createSignal<Array<Author | Topic>>([])
|
||||||
const [subscriptionFilter, setSubscriptionFilter] = createSignal<SubscriptionFilter>('all')
|
const [followsFilter, setFollowsFilter] = createSignal<FollowsFilter>('all')
|
||||||
const [searchQuery, setSearchQuery] = createSignal('')
|
const [searchQuery, setSearchQuery] = createSignal('')
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => setFlatFollows([...(follows?.authors || []), ...(follows?.topics || [])]))
|
||||||
const { authors, topics } = subscriptions
|
|
||||||
if (authors || topics) {
|
createEffect(
|
||||||
const fdata = [...(authors || []), ...(topics || [])]
|
on([flatFollows, followsFilter], ([flat, mode]) => {
|
||||||
setFollowing(fdata)
|
if (mode === 'authors') {
|
||||||
if (subscriptionFilter() === 'authors') {
|
setFiltered(flat.filter((s) => 'name' in s))
|
||||||
setFiltered(fdata.filter((s) => 'name' in s))
|
} else if (mode === 'topics') {
|
||||||
} else if (subscriptionFilter() === 'topics') {
|
setFiltered(flat.filter((s) => 'title' in s))
|
||||||
setFiltered(fdata.filter((s) => 'title' in s))
|
|
||||||
} else {
|
} else {
|
||||||
setFiltered(fdata)
|
setFiltered(flat)
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
})
|
{ defer: true },
|
||||||
|
)
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (searchQuery()) {
|
if (searchQuery()) {
|
||||||
setFiltered(dummyFilter(following(), searchQuery(), lang()))
|
setFiltered(dummyFilter(flatFollows(), searchQuery(), lang()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -60,32 +59,32 @@ export const ProfileSubscriptions = () => {
|
||||||
<div class="col-md-20 col-lg-18 col-xl-16">
|
<div class="col-md-20 col-lg-18 col-xl-16">
|
||||||
<h1>{t('My subscriptions')}</h1>
|
<h1>{t('My subscriptions')}</h1>
|
||||||
<p class="description">{t('Here you can manage all your Discours subscriptions')}</p>
|
<p class="description">{t('Here you can manage all your Discours subscriptions')}</p>
|
||||||
<Show when={following()} fallback={<Loading />}>
|
<Show when={flatFollows()} fallback={<Loading />}>
|
||||||
<ul class="view-switcher">
|
<ul class="view-switcher">
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
'view-switcher__item--selected': subscriptionFilter() === 'all',
|
'view-switcher__item--selected': followsFilter() === 'all',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button type="button" onClick={() => setSubscriptionFilter('all')}>
|
<button type="button" onClick={() => setFollowsFilter('all')}>
|
||||||
{t('All')}
|
{t('All')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
'view-switcher__item--selected': subscriptionFilter() === 'authors',
|
'view-switcher__item--selected': followsFilter() === 'authors',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button type="button" onClick={() => setSubscriptionFilter('authors')}>
|
<button type="button" onClick={() => setFollowsFilter('authors')}>
|
||||||
{t('Authors')}
|
{t('Authors')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
'view-switcher__item--selected': subscriptionFilter() === 'topics',
|
'view-switcher__item--selected': followsFilter() === 'topics',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button type="button" onClick={() => setSubscriptionFilter('topics')}>
|
<button type="button" onClick={() => setFollowsFilter('topics')}>
|
||||||
{t('Topics')}
|
{t('Topics')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
@ -104,9 +103,9 @@ export const ProfileSubscriptions = () => {
|
||||||
{(followingItem) => (
|
{(followingItem) => (
|
||||||
<div>
|
<div>
|
||||||
{isAuthor(followingItem) ? (
|
{isAuthor(followingItem) ? (
|
||||||
<AuthorBadge minimizeSubscribeButton={true} author={followingItem} />
|
<AuthorBadge minimize={true} author={followingItem} />
|
||||||
) : (
|
) : (
|
||||||
<TopicBadge minimizeSubscribeButton={true} topic={followingItem} />
|
<TopicBadge minimize={true} topic={followingItem} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export { BadgeSubscribeButton } from './BadgeSubscribeButton'
|
|
|
@ -175,7 +175,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.subscribed {
|
&.followed {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|
||||||
|
@ -192,4 +192,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,35 +2,36 @@ import { clsx } from 'clsx'
|
||||||
import { Show, createMemo } from 'solid-js'
|
import { Show, createMemo } from 'solid-js'
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
import { Button } from '../Button'
|
import { Button } from '../Button'
|
||||||
import stylesButton from '../Button/Button.module.scss'
|
|
||||||
import { CheckButton } from '../CheckButton'
|
import { CheckButton } from '../CheckButton'
|
||||||
import { Icon } from '../Icon'
|
import { Icon } from '../Icon'
|
||||||
import styles from './BadgeDubscribeButton.module.scss'
|
|
||||||
|
import stylesButton from '../Button/Button.module.scss'
|
||||||
|
import styles from './FollowingButton.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string
|
class?: string
|
||||||
isSubscribed: boolean
|
isFollowed: boolean
|
||||||
minimizeSubscribeButton?: boolean
|
minimize?: boolean
|
||||||
action: () => void
|
action: () => void
|
||||||
iconButtons?: boolean
|
iconButtons?: boolean
|
||||||
actionMessageType?: 'subscribe' | 'unsubscribe'
|
actionMessageType?: 'follow' | 'unfollow'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BadgeSubscribeButton = (props: Props) => {
|
export const FollowingButton = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
|
|
||||||
const inActionText = createMemo(() => {
|
const inActionText = createMemo(() => {
|
||||||
return props.actionMessageType === 'subscribe' ? t('Subscribing...') : t('Unsubscribing...')
|
return props.actionMessageType === 'follow' ? t('Following...') : t('Unfollowing...')
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={props.class}>
|
<div class={props.class}>
|
||||||
<Show
|
<Show
|
||||||
when={!props.minimizeSubscribeButton}
|
when={!props.minimize}
|
||||||
fallback={<CheckButton text={t('Follow')} checked={props.isSubscribed} onClick={props.action} />}
|
fallback={<CheckButton text={t('Follow')} checked={props.isFollowed} onClick={props.action} />}
|
||||||
>
|
>
|
||||||
<Show
|
<Show
|
||||||
when={props.isSubscribed}
|
when={props.isFollowed}
|
||||||
fallback={
|
fallback={
|
||||||
<Button
|
<Button
|
||||||
variant={props.iconButtons ? 'secondary' : 'bordered'}
|
variant={props.iconButtons ? 'secondary' : 'bordered'}
|
||||||
|
@ -38,7 +39,7 @@ export const BadgeSubscribeButton = (props: Props) => {
|
||||||
value={
|
value={
|
||||||
<Show
|
<Show
|
||||||
when={props.iconButtons}
|
when={props.iconButtons}
|
||||||
fallback={props.actionMessageType ? inActionText() : t('Subscribe')}
|
fallback={props.actionMessageType ? inActionText() : t('Follow')}
|
||||||
>
|
>
|
||||||
<Icon name="author-subscribe" class={stylesButton.icon} />
|
<Icon name="author-subscribe" class={stylesButton.icon} />
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -47,7 +48,7 @@ export const BadgeSubscribeButton = (props: Props) => {
|
||||||
isSubscribeButton={true}
|
isSubscribeButton={true}
|
||||||
class={clsx(styles.actionButton, {
|
class={clsx(styles.actionButton, {
|
||||||
[styles.iconed]: props.iconButtons,
|
[styles.iconed]: props.iconButtons,
|
||||||
[stylesButton.subscribed]: props.isSubscribed,
|
[stylesButton.followed]: props.isFollowed,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -76,7 +77,7 @@ export const BadgeSubscribeButton = (props: Props) => {
|
||||||
isSubscribeButton={true}
|
isSubscribeButton={true}
|
||||||
class={clsx(styles.actionButton, {
|
class={clsx(styles.actionButton, {
|
||||||
[styles.iconed]: props.iconButtons,
|
[styles.iconed]: props.iconButtons,
|
||||||
[stylesButton.subscribed]: props.isSubscribed,
|
[stylesButton.followed]: props.isFollowed,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
1
src/components/_shared/FollowingButton/index.ts
Normal file
1
src/components/_shared/FollowingButton/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { FollowingButton } from './FollowingButton'
|
|
@ -1,30 +1,27 @@
|
||||||
import { Accessor, JSX, createContext, createEffect, createSignal, useContext } from 'solid-js'
|
import { Accessor, JSX, createContext, createEffect, createSignal, on, useContext } from 'solid-js'
|
||||||
import { createStore } from 'solid-js/store'
|
import { createStore } from 'solid-js/store'
|
||||||
|
|
||||||
import { apiClient } from '../graphql/client/core'
|
import { apiClient } from '../graphql/client/core'
|
||||||
import { Author, AuthorFollowsResult, Community, FollowingEntity, Topic } from '../graphql/schema/core.gen'
|
import { Author, AuthorFollowsResult, FollowingEntity } from '../graphql/schema/core.gen'
|
||||||
|
|
||||||
import { useSession } from './session'
|
import { useSession } from './session'
|
||||||
|
|
||||||
export type SubscriptionsData = {
|
type FollowingData = { slug: string; type: 'follow' | 'unfollow' }
|
||||||
topics?: Topic[]
|
|
||||||
authors?: Author[]
|
|
||||||
communities?: Community[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubscribeAction = { slug: string; type: 'subscribe' | 'unsubscribe' }
|
|
||||||
|
|
||||||
interface FollowingContextType {
|
interface FollowingContextType {
|
||||||
loading: Accessor<boolean>
|
loading: Accessor<boolean>
|
||||||
|
|
||||||
followers: Accessor<Author[]>
|
followers: Accessor<Author[]>
|
||||||
subscriptions: AuthorFollowsResult
|
setFollows: (follows: AuthorFollowsResult) => void
|
||||||
setSubscriptions: (subscriptions: AuthorFollowsResult) => void
|
|
||||||
setFollowing: (what: FollowingEntity, slug: string, value: boolean) => void
|
following: Accessor<FollowingData>
|
||||||
loadSubscriptions: () => void
|
changeFollowing: (what: FollowingEntity, slug: string, value: boolean) => void
|
||||||
|
|
||||||
|
follows: AuthorFollowsResult
|
||||||
|
loadFollows: () => void
|
||||||
|
|
||||||
follow: (what: FollowingEntity, slug: string) => Promise<void>
|
follow: (what: FollowingEntity, slug: string) => Promise<void>
|
||||||
unfollow: (what: FollowingEntity, slug: string) => Promise<void>
|
unfollow: (what: FollowingEntity, slug: string) => Promise<void>
|
||||||
// followers: Accessor<Author[]>
|
|
||||||
subscribeInAction?: Accessor<SubscribeAction>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FollowingContext = createContext<FollowingContextType>()
|
const FollowingContext = createContext<FollowingContextType>()
|
||||||
|
@ -42,7 +39,7 @@ const EMPTY_SUBSCRIPTIONS: AuthorFollowsResult = {
|
||||||
export const FollowingProvider = (props: { children: JSX.Element }) => {
|
export const FollowingProvider = (props: { children: JSX.Element }) => {
|
||||||
const [loading, setLoading] = createSignal<boolean>(false)
|
const [loading, setLoading] = createSignal<boolean>(false)
|
||||||
const [followers, setFollowers] = createSignal<Author[]>([])
|
const [followers, setFollowers] = createSignal<Author[]>([])
|
||||||
const [subscriptions, setSubscriptions] = createStore<AuthorFollowsResult>(EMPTY_SUBSCRIPTIONS)
|
const [follows, setFollows] = createStore<AuthorFollowsResult>(EMPTY_SUBSCRIPTIONS)
|
||||||
const { author, session } = useSession()
|
const { author, session } = useSession()
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
|
@ -51,7 +48,7 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
|
||||||
if (apiClient.private) {
|
if (apiClient.private) {
|
||||||
console.debug('[context.following] fetching subs data...')
|
console.debug('[context.following] fetching subs data...')
|
||||||
const result = await apiClient.getAuthorFollows({ user: session()?.user.id })
|
const result = await apiClient.getAuthorFollows({ user: session()?.user.id })
|
||||||
setSubscriptions(result || EMPTY_SUBSCRIPTIONS)
|
setFollows(result || EMPTY_SUBSCRIPTIONS)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.info('[context.following] cannot get subs', error)
|
console.info('[context.following] cannot get subs', error)
|
||||||
|
@ -60,59 +57,65 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
const [following, setFollowing] = createSignal<FollowingData>()
|
||||||
console.info('[context.following] subs:', subscriptions)
|
|
||||||
})
|
|
||||||
|
|
||||||
const [subscribeInAction, setSubscribeInAction] = createSignal<SubscribeAction>()
|
|
||||||
const follow = async (what: FollowingEntity, slug: string) => {
|
const follow = async (what: FollowingEntity, slug: string) => {
|
||||||
if (!author()) return
|
if (!author()) return
|
||||||
setSubscribeInAction({ slug, type: 'subscribe' })
|
setFollowing({ slug, type: 'follow' })
|
||||||
try {
|
try {
|
||||||
const subscriptionData = await apiClient.follow({ what, slug })
|
const result = await apiClient.follow({ what, slug })
|
||||||
setSubscriptions((prevSubscriptions) => {
|
setFollows((subs) => {
|
||||||
if (!prevSubscriptions[what]) prevSubscriptions[what] = []
|
if (result.authors) subs['authors'] = result.authors || []
|
||||||
prevSubscriptions[what].push(subscriptionData)
|
if (result.topics) subs['topics'] = result.topics || []
|
||||||
return prevSubscriptions
|
return subs
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
setSubscribeInAction() // Сбрасываем состояние действия подписки.
|
setFollowing() // Сбрасываем состояние действия подписки.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unfollow = async (what: FollowingEntity, slug: string) => {
|
const unfollow = async (what: FollowingEntity, slug: string) => {
|
||||||
if (!author()) return
|
if (!author()) return
|
||||||
setSubscribeInAction({ slug: slug, type: 'unsubscribe' })
|
setFollowing({ slug: slug, type: 'unfollow' })
|
||||||
try {
|
try {
|
||||||
await apiClient.unfollow({ what, slug })
|
const result = await apiClient.unfollow({ what, slug })
|
||||||
|
setFollows((subs) => {
|
||||||
|
if (result.authors) subs['authors'] = result.authors || []
|
||||||
|
if (result.topics) subs['topics'] = result.topics || []
|
||||||
|
return subs
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
setSubscribeInAction()
|
setFollowing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(
|
||||||
if (author()) {
|
on(
|
||||||
try {
|
() => author(),
|
||||||
const appdata = session()?.user.app_data
|
(a) => {
|
||||||
if (appdata) {
|
if (a?.id) {
|
||||||
const { authors, followers, topics } = appdata
|
try {
|
||||||
setSubscriptions({ authors, topics })
|
const appdata = session()?.user.app_data
|
||||||
setFollowers(followers)
|
if (appdata) {
|
||||||
if (!authors) fetchData()
|
const { authors, followers, topics } = appdata
|
||||||
|
setFollows({ authors, topics })
|
||||||
|
setFollowers(followers)
|
||||||
|
if (!authors) fetchData()
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
},
|
||||||
console.error(e)
|
),
|
||||||
}
|
)
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const setFollowing = (what: FollowingEntity, slug: string, value = true) => {
|
const changeFollowing = (what: FollowingEntity, slug: string, value = true) => {
|
||||||
setSubscriptions((prevSubscriptions) => {
|
setFollows((fff) => {
|
||||||
const updatedSubs = { ...prevSubscriptions }
|
const updatedSubs = { ...fff }
|
||||||
if (!updatedSubs[what]) updatedSubs[what] = []
|
if (!updatedSubs[what]) updatedSubs[what] = []
|
||||||
if (value) {
|
if (value) {
|
||||||
const exists = updatedSubs[what]?.some((entity) => entity.slug === slug)
|
const exists = updatedSubs[what]?.some((entity) => entity.slug === slug)
|
||||||
|
@ -133,15 +136,14 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
|
||||||
|
|
||||||
const value: FollowingContextType = {
|
const value: FollowingContextType = {
|
||||||
loading,
|
loading,
|
||||||
subscriptions,
|
follows,
|
||||||
setSubscriptions,
|
setFollows,
|
||||||
setFollowing,
|
following,
|
||||||
|
changeFollowing,
|
||||||
followers,
|
followers,
|
||||||
loadSubscriptions: fetchData,
|
loadFollows: fetchData,
|
||||||
follow,
|
follow,
|
||||||
unfollow,
|
unfollow,
|
||||||
// followers,
|
|
||||||
subscribeInAction,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <FollowingContext.Provider value={value}>{props.children}</FollowingContext.Provider>
|
return <FollowingContext.Provider value={value}>{props.children}</FollowingContext.Provider>
|
||||||
|
|
|
@ -6,7 +6,24 @@ export default gql`
|
||||||
error
|
error
|
||||||
authors {
|
authors {
|
||||||
id
|
id
|
||||||
|
name
|
||||||
slug
|
slug
|
||||||
|
pic
|
||||||
|
bio
|
||||||
|
stat {
|
||||||
|
followers
|
||||||
|
shouts
|
||||||
|
comments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
topics {
|
||||||
|
body
|
||||||
|
slug
|
||||||
|
stat {
|
||||||
|
shouts
|
||||||
|
authors
|
||||||
|
followers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,27 @@ export default gql`
|
||||||
mutation UnfollowMutation($what: FollowingEntity!, $slug: String!) {
|
mutation UnfollowMutation($what: FollowingEntity!, $slug: String!) {
|
||||||
unfollow(what: $what, slug: $slug) {
|
unfollow(what: $what, slug: $slug) {
|
||||||
error
|
error
|
||||||
|
authors {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
pic
|
||||||
|
bio
|
||||||
|
stat {
|
||||||
|
followers
|
||||||
|
shouts
|
||||||
|
comments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
topics {
|
||||||
|
body
|
||||||
|
slug
|
||||||
|
stat {
|
||||||
|
shouts
|
||||||
|
authors
|
||||||
|
followers
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
|
@ -53,4 +53,4 @@ export type UploadedFile = {
|
||||||
originalFilename?: string
|
originalFilename?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SubscriptionFilter = 'all' | 'authors' | 'topics' | 'communities'
|
export type FollowsFilter = 'all' | 'authors' | 'topics' | 'communities'
|
||||||
|
|
|
@ -14,8 +14,9 @@ const cssModuleHMR = () => {
|
||||||
const { modules } = context
|
const { modules } = context
|
||||||
|
|
||||||
modules.forEach((module) => {
|
modules.forEach((module) => {
|
||||||
if (module.id.includes('.module.scss')) {
|
if (module.id.includes('.scss') || module.id.includes('.css')) {
|
||||||
module.isSelfAccepting = true
|
module.isSelfAccepting = true
|
||||||
|
module.accept()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user