Merge pull request #403 from Discours/hotfix/topicSubscibtionsFix
Fix topic subscriptions status
This commit is contained in:
commit
b3e3068a8d
|
@ -16,13 +16,10 @@ import { ConditionalWrapper } from '../../_shared/ConditionalWrapper'
|
|||
import { Icon } from '../../_shared/Icon'
|
||||
import { Userpic } from '../Userpic'
|
||||
|
||||
import { FollowedInfo } from '../../../pages/types'
|
||||
import stylesButton from '../../_shared/Button/Button.module.scss'
|
||||
import styles from './AuthorBadge.module.scss'
|
||||
|
||||
type FollowedInfo = {
|
||||
value?: boolean
|
||||
loaded?: boolean
|
||||
}
|
||||
type Props = {
|
||||
author: Author
|
||||
minimizeSubscribeButton?: boolean
|
||||
|
|
|
@ -308,7 +308,13 @@ export const AuthorCard = (props: Props) => {
|
|||
author={subscription}
|
||||
/>
|
||||
) : (
|
||||
<TopicBadge topic={subscription} />
|
||||
<TopicBadge
|
||||
isFollowed={{
|
||||
loaded: Boolean(authorSubs()),
|
||||
value: isOwnerSubscribed(subscription.id),
|
||||
}}
|
||||
topic={subscription}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</For>
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
.TopicBadge {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 2rem;
|
||||
gap: 1rem;
|
||||
|
||||
.content {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: .8rem;
|
||||
}
|
||||
.basicInfo {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
|
@ -78,3 +81,34 @@
|
|||
width: 9em;
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
@include font-size(1.5rem);
|
||||
|
||||
color: var(--secondary-color);
|
||||
display: flex;
|
||||
margin: 0 0 1em;
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.statsItem {
|
||||
@include font-size(1.4rem);
|
||||
|
||||
margin-right: 1.6rem;
|
||||
white-space: nowrap;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.followers {
|
||||
word-break: keep-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 { useLocalize } from '../../../context/localize'
|
||||
|
@ -11,11 +11,14 @@ import { getImageUrl } from '../../../utils/getImageUrl'
|
|||
import { Button } from '../../_shared/Button'
|
||||
import { CheckButton } from '../../_shared/CheckButton'
|
||||
|
||||
import { FollowedInfo } from '../../../pages/types'
|
||||
import styles from './TopicBadge.module.scss'
|
||||
|
||||
type Props = {
|
||||
topic: Topic
|
||||
minimizeSubscribeButton?: boolean
|
||||
isFollowed?: FollowedInfo
|
||||
showStat?: boolean
|
||||
}
|
||||
|
||||
export const TopicBadge = (props: Props) => {
|
||||
|
@ -24,12 +27,12 @@ export const TopicBadge = (props: Props) => {
|
|||
const [isMobileView, setIsMobileView] = createSignal(false)
|
||||
const { requireAuthentication } = useSession()
|
||||
const { setFollowing, loading: subLoading } = useFollowing()
|
||||
const [followed, setFollowed] = createSignal()
|
||||
const [isFollowed, setIsFollowed] = createSignal<boolean>()
|
||||
|
||||
const handleFollowClick = () => {
|
||||
const value = !followed()
|
||||
const value = !isFollowed()
|
||||
requireAuthentication(() => {
|
||||
setFollowed(value)
|
||||
setIsFollowed(value)
|
||||
setFollowing(FollowingEntity.Topic, props.topic.slug, value)
|
||||
}, 'subscribe')
|
||||
}
|
||||
|
@ -38,67 +41,85 @@ export const TopicBadge = (props: Props) => {
|
|||
setIsMobileView(!mediaMatches.sm)
|
||||
})
|
||||
|
||||
createEffect(
|
||||
on(
|
||||
() => props.isFollowed,
|
||||
() => {
|
||||
setIsFollowed(props.isFollowed.value)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
const title = () =>
|
||||
lang() === 'en' ? capitalize(props.topic.slug.replaceAll('-', ' ')) : props.topic.title
|
||||
|
||||
return (
|
||||
<div class={styles.TopicBadge}>
|
||||
<div class={styles.basicInfo}>
|
||||
<a
|
||||
href={`/topic/${props.topic.slug}`}
|
||||
class={clsx(styles.picture, {
|
||||
[styles.withImage]: props.topic.pic,
|
||||
[styles.smallSize]: isMobileView(),
|
||||
})}
|
||||
style={
|
||||
props.topic.pic && {
|
||||
'background-image': `url('${getImageUrl(props.topic.pic, { width: 40, height: 40 })}')`,
|
||||
<div class={styles.content}>
|
||||
<div class={styles.basicInfo}>
|
||||
<a
|
||||
href={`/topic/${props.topic.slug}`}
|
||||
class={clsx(styles.picture, {
|
||||
[styles.withImage]: props.topic.pic,
|
||||
[styles.smallSize]: isMobileView(),
|
||||
})}
|
||||
style={
|
||||
props.topic.pic && {
|
||||
'background-image': `url('${getImageUrl(props.topic.pic, { width: 40, height: 40 })}')`,
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
<a href={`/topic/${props.topic.slug}`} class={styles.info}>
|
||||
<span class={styles.title}>{title()}</span>
|
||||
/>
|
||||
<a href={`/topic/${props.topic.slug}`} class={styles.info}>
|
||||
<span class={styles.title}>{title()}</span>
|
||||
<Show
|
||||
when={props.topic.body}
|
||||
fallback={
|
||||
<div class={styles.description}>
|
||||
{t('PublicationsWithCount', { count: props.topic.stat.shouts ?? 0 })}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div class={clsx('text-truncate', styles.description)}>{props.topic.body}</div>
|
||||
</Show>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class={styles.actions}>
|
||||
<Show
|
||||
when={props.topic.body}
|
||||
when={!props.minimizeSubscribeButton}
|
||||
fallback={
|
||||
<div class={styles.description}>
|
||||
{t('PublicationsWithCount', { count: props.topic.stat.shouts ?? 0 })}
|
||||
</div>
|
||||
<CheckButton text={t('Follow')} checked={Boolean(isFollowed())} onClick={handleFollowClick} />
|
||||
}
|
||||
>
|
||||
<div class={clsx('text-truncate', styles.description)}>{props.topic.body}</div>
|
||||
</Show>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class={styles.actions}>
|
||||
<Show
|
||||
when={!props.minimizeSubscribeButton}
|
||||
fallback={
|
||||
<CheckButton text={t('Follow')} checked={Boolean(followed())} onClick={handleFollowClick} />
|
||||
}
|
||||
>
|
||||
<Show
|
||||
when={followed()}
|
||||
fallback={
|
||||
<Show
|
||||
when={isFollowed()}
|
||||
fallback={
|
||||
<Button
|
||||
variant="primary"
|
||||
size="S"
|
||||
value={subLoading() ? t('subscribing...') : t('Subscribe')}
|
||||
onClick={handleFollowClick}
|
||||
class={styles.subscribeButton}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="S"
|
||||
value={subLoading() ? t('subscribing...') : t('Subscribe')}
|
||||
onClick={handleFollowClick}
|
||||
variant="bordered"
|
||||
size="S"
|
||||
value={t('Following')}
|
||||
class={styles.subscribeButton}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
onClick={handleFollowClick}
|
||||
variant="bordered"
|
||||
size="S"
|
||||
value={t('Following')}
|
||||
class={styles.subscribeButton}
|
||||
/>
|
||||
</Show>
|
||||
</Show>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<div class={styles.stats}>
|
||||
<span class={styles.statsItem}>{t('shoutsWithCount', { count: props.topic?.stat?.shouts })}</span>
|
||||
<span class={styles.statsItem}>{t('authorsWithCount', { count: props.topic?.stat?.authors })}</span>
|
||||
<span class={styles.statsItem}>
|
||||
{t('followersWithCount', { count: props.topic?.stat?.followers })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -32,45 +32,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
@include font-size(1.7rem);
|
||||
|
||||
color: #9fa1a7;
|
||||
display: flex;
|
||||
margin: 0 0 1em;
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.statsItem {
|
||||
@include font-size(1.5rem);
|
||||
|
||||
margin-right: 1.6rem;
|
||||
white-space: nowrap;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.compact {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
&.followers {
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
&.button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loadMoreContainer {
|
||||
margin-top: 48px;
|
||||
text-align: center;
|
|
@ -1,21 +1,22 @@
|
|||
import type { Topic } from '../../graphql/schema/core.gen'
|
||||
import type { Topic } from '../../../graphql/schema/core.gen'
|
||||
|
||||
import { Meta } from '@solidjs/meta'
|
||||
import { clsx } from 'clsx'
|
||||
import { For, Show, createEffect, createMemo, createSignal } from 'solid-js'
|
||||
|
||||
import { useFollowing } from '../../context/following'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useRouter } from '../../stores/router'
|
||||
import { setTopicsSort, useTopicsStore } from '../../stores/zine/topics'
|
||||
import { capitalize } from '../../utils/capitalize'
|
||||
import { dummyFilter } from '../../utils/dummyFilter'
|
||||
import { getImageUrl } from '../../utils/getImageUrl'
|
||||
import { scrollHandler } from '../../utils/scroll'
|
||||
import { TopicCard } from '../Topic/Card'
|
||||
import { Loading } from '../_shared/Loading'
|
||||
import { SearchField } from '../_shared/SearchField'
|
||||
import { useFollowing } from '../../../context/following'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { useRouter } from '../../../stores/router'
|
||||
import { setTopicsSort, useTopicsStore } from '../../../stores/zine/topics'
|
||||
import { capitalize } from '../../../utils/capitalize'
|
||||
import { dummyFilter } from '../../../utils/dummyFilter'
|
||||
import { getImageUrl } from '../../../utils/getImageUrl'
|
||||
import { scrollHandler } from '../../../utils/scroll'
|
||||
import { TopicCard } from '../../Topic/Card'
|
||||
import { Loading } from '../../_shared/Loading'
|
||||
import { SearchField } from '../../_shared/SearchField'
|
||||
|
||||
import { TopicBadge } from '../../Topic/TopicBadge'
|
||||
import styles from './AllTopics.module.scss'
|
||||
|
||||
type AllTopicsPageSearchParams = {
|
||||
|
@ -29,7 +30,7 @@ type Props = {
|
|||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export const AllTopicsView = (props: Props) => {
|
||||
export const AllTopics = (props: Props) => {
|
||||
const { t, lang } = useLocalize()
|
||||
const { searchParams, changeSearchParams } = useRouter<AllTopicsPageSearchParams>()
|
||||
const [limit, setLimit] = createSignal(PAGE_SIZE)
|
||||
|
@ -41,8 +42,6 @@ export const AllTopicsView = (props: Props) => {
|
|||
sortBy: searchParams().by || 'shouts',
|
||||
})
|
||||
|
||||
const { subscriptions } = useFollowing()
|
||||
|
||||
createEffect(() => {
|
||||
if (!searchParams().by) {
|
||||
changeSearchParams({
|
||||
|
@ -76,7 +75,7 @@ export const AllTopicsView = (props: Props) => {
|
|||
return keys
|
||||
})
|
||||
|
||||
const subscribed = (topicSlug: string) => subscriptions.topics.some((topic) => topic.slug === topicSlug)
|
||||
const { isOwnerSubscribed } = useFollowing()
|
||||
|
||||
const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
|
||||
const [searchQuery, setSearchQuery] = createSignal('')
|
||||
|
@ -186,28 +185,18 @@ export const AllTopicsView = (props: Props) => {
|
|||
|
||||
<Show when={searchParams().by && searchParams().by !== 'title'}>
|
||||
<div class="row">
|
||||
<div class="col-lg-20 col-xl-18">
|
||||
<div class="col-lg-20 col-xl-18 py-4">
|
||||
<For each={filteredResults().slice(0, limit())}>
|
||||
{(topic) => (
|
||||
<>
|
||||
<TopicCard
|
||||
<TopicBadge
|
||||
topic={topic}
|
||||
compact={false}
|
||||
subscribed={subscribed(topic.slug)}
|
||||
showPublications={true}
|
||||
showDescription={true}
|
||||
isFollowed={{
|
||||
loaded: filteredResults().length > 0,
|
||||
value: isOwnerSubscribed(topic.slug),
|
||||
}}
|
||||
showStat={true}
|
||||
/>
|
||||
<div class={styles.stats}>
|
||||
<span class={styles.statsItem}>
|
||||
{t('shoutsWithCount', { count: topic.stat.shouts })}
|
||||
</span>
|
||||
<span class={styles.statsItem}>
|
||||
{t('authorsWithCount', { count: topic.stat.authors })}
|
||||
</span>
|
||||
<span class={styles.statsItem}>
|
||||
{t('followersWithCount', { count: topic.stat.followers })}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</For>
|
1
src/components/Views/AllTopics/index.ts
Normal file
1
src/components/Views/AllTopics/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { AllTopics } from './AllTopics'
|
|
@ -128,7 +128,6 @@ export const AuthorView = (props: Props) => {
|
|||
const data = await apiClient.getReactionsBy({
|
||||
by: { comment: false, created_by: commenter.id },
|
||||
})
|
||||
console.debug('[components.Author] fetched comments', data)
|
||||
setCommented(data)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ interface FollowingContextType {
|
|||
loadSubscriptions: () => void
|
||||
follow: (what: FollowingEntity, slug: string) => Promise<void>
|
||||
unfollow: (what: FollowingEntity, slug: string) => Promise<void>
|
||||
isOwnerSubscribed: (userId: number) => boolean
|
||||
isOwnerSubscribed: (id: number | string) => boolean
|
||||
}
|
||||
|
||||
const FollowingContext = createContext<FollowingContextType>()
|
||||
|
@ -109,9 +109,11 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const isOwnerSubscribed = (userId: number) => {
|
||||
if (!author()) return
|
||||
return !!subscriptions?.authors?.some((authorEntity) => authorEntity.id === userId)
|
||||
const isOwnerSubscribed = (id?: number | string) => {
|
||||
if (!author() || !subscriptions) return
|
||||
const isAuthorSubscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === id)
|
||||
const isTopicSubscribed = subscriptions.topics?.some((topicEntity) => topicEntity.slug === id)
|
||||
return !!isAuthorSubscribed || !!isTopicSubscribed
|
||||
}
|
||||
|
||||
const value: FollowingContextType = {
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { PageProps } from './types'
|
|||
|
||||
import { createSignal, onMount } from 'solid-js'
|
||||
|
||||
import { AllTopicsView } from '../components/Views/AllTopics'
|
||||
import { AllTopics } from '../components/Views/AllTopics'
|
||||
import { PageLayout } from '../components/_shared/PageLayout'
|
||||
import { useLocalize } from '../context/localize'
|
||||
import { loadAllTopics } from '../stores/zine/topics'
|
||||
|
@ -23,7 +23,7 @@ export const AllTopicsPage = (props: PageProps) => {
|
|||
|
||||
return (
|
||||
<PageLayout title={t('Themes and plots')}>
|
||||
<AllTopicsView isLoaded={isLoaded()} topics={props.allTopics} />
|
||||
<AllTopics isLoaded={isLoaded()} topics={props.allTopics} />
|
||||
</PageLayout>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -50,4 +50,9 @@ export type UploadedFile = {
|
|||
originalFilename?: string
|
||||
}
|
||||
|
||||
export type FollowedInfo = {
|
||||
value?: boolean
|
||||
loaded?: boolean
|
||||
}
|
||||
|
||||
export type SubscriptionFilter = 'all' | 'authors' | 'topics' | 'communities'
|
||||
|
|
Loading…
Reference in New Issue
Block a user