This commit is contained in:
ilya-bkv 2024-03-15 15:58:22 +03:00
parent b44008b229
commit 5c4d605724
9 changed files with 193 additions and 191 deletions

View File

@ -89,34 +89,4 @@
text-align: right; text-align: right;
} }
} }
.actionButton {
border-radius: 0.8rem !important;
margin-right: 0 !important;
width: 9em;
&.iconed {
padding: 6px !important;
min-width: 4rem;
width: unset;
&:hover img {
filter: invert(1);
}
}
&:hover {
.actionButtonLabel {
display: none;
}
.actionButtonLabelHovered {
display: block;
}
}
}
.actionButtonLabelHovered {
display: none;
}
} }

View File

@ -15,10 +15,9 @@ import { CheckButton } from '../../_shared/CheckButton'
import { ConditionalWrapper } from '../../_shared/ConditionalWrapper' import { ConditionalWrapper } from '../../_shared/ConditionalWrapper'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
import { Userpic } from '../Userpic' import { Userpic } from '../Userpic'
import { FollowedInfo } from '../../../pages/types'
import stylesButton from '../../_shared/Button/Button.module.scss' import stylesButton from '../../_shared/Button/Button.module.scss'
import styles from './AuthorBadge.module.scss' import styles from './AuthorBadge.module.scss'
import { BadgeSubscribeButton } from "../../_shared/BadgeSubscribeButton";
type Props = { type Props = {
author: Author author: Author
@ -29,13 +28,19 @@ type Props = {
inviteView?: boolean inviteView?: boolean
onInvite?: (id: number) => void onInvite?: (id: number) => void
selected?: boolean selected?: boolean
isFollowed?: FollowedInfo
} }
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 [isMobileView, setIsMobileView] = createSignal(false) const [isMobileView, setIsMobileView] = createSignal(false)
const [isFollowed, setIsFollowed] = createSignal<boolean>() const [isSubscribed, setIsSubscribed] = createSignal<boolean>()
createEffect(() => {
if(!subscriptions || !props.author) return
const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.author?.id);
setIsSubscribed(subscribed)
})
createEffect(() => { createEffect(() => {
setIsMobileView(!mediaMatches.sm) setIsMobileView(!mediaMatches.sm)
@ -67,20 +72,9 @@ export const AuthorBadge = (props: Props) => {
return props.author.name return props.author.name
}) })
createEffect(
on(
() => props.isFollowed,
() => {
setIsFollowed(props.isFollowed?.value)
},
),
)
const handleFollowClick = () => { const handleFollowClick = () => {
const value = !isFollowed()
requireAuthentication(() => { requireAuthentication(() => {
setIsFollowed(value) isSubscribed() ? unfollow(FollowingEntity.Author, props.author.slug) : follow(FollowingEntity.Author, props.author.slug)
setFollowing(FollowingEntity.Author, props.author.slug, value)
}, 'subscribe') }, 'subscribe')
} }
@ -134,55 +128,59 @@ 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}>
<Show <BadgeSubscribeButton
when={!props.minimizeSubscribeButton} action={() => handleFollowClick()}
fallback={<CheckButton text={t('Follow')} checked={isFollowed()} onClick={handleFollowClick} />} isSubscribed={isSubscribed()}
>
<Show
when={isFollowed()}
fallback={
<Button
variant={props.iconButtons ? 'secondary' : 'bordered'}
size="S"
value={
<Show when={props.iconButtons} fallback={t('Subscribe')}>
<Icon name="author-subscribe" class={stylesButton.icon} />
</Show>
}
onClick={handleFollowClick}
isSubscribeButton={true}
class={clsx(styles.actionButton, {
[styles.iconed]: props.iconButtons,
[stylesButton.subscribed]: isFollowed(),
})}
/> />
} {/*<Show*/}
> {/* when={!props.minimizeSubscribeButton}*/}
<Button {/* fallback={<CheckButton text={t('Follow')} checked={isSubscribed()} onClick={handleFollowClick} />}*/}
variant={props.iconButtons ? 'secondary' : 'bordered'} {/*>*/}
size="S" {/* <Show*/}
value={ {/* when={isSubscribed()}*/}
<Show {/* fallback={*/}
when={props.iconButtons} {/* <Button*/}
fallback={ {/* variant={props.iconButtons ? 'secondary' : 'bordered'}*/}
<> {/* size="S"*/}
<span class={styles.actionButtonLabel}>{t('Following')}</span> {/* value={*/}
<span class={styles.actionButtonLabelHovered}>{t('Unfollow')}</span> {/* <Show when={props.iconButtons} fallback={t('Subscribe')}>*/}
</> {/* <Icon name="author-subscribe" class={stylesButton.icon} />*/}
} {/* </Show>*/}
> {/* }*/}
<Icon name="author-unsubscribe" class={stylesButton.icon} /> {/* onClick={handleFollowClick}*/}
</Show> {/* isSubscribeButton={true}*/}
} {/* class={clsx(styles.actionButton, {*/}
onClick={handleFollowClick} {/* [styles.iconed]: props.iconButtons,*/}
isSubscribeButton={true} {/* [stylesButton.subscribed]: isSubscribed(),*/}
class={clsx(styles.actionButton, { {/* })}*/}
[styles.iconed]: props.iconButtons, {/* />*/}
[stylesButton.subscribed]: isFollowed(), {/* }*/}
})} {/* >*/}
/> {/* <Button*/}
</Show> {/* variant={props.iconButtons ? 'secondary' : 'bordered'}*/}
</Show> {/* size="S"*/}
{/* value={*/}
{/* <Show*/}
{/* when={props.iconButtons}*/}
{/* fallback={*/}
{/* <>*/}
{/* <span class={styles.actionButtonLabel}>{t('Following')}</span>*/}
{/* <span class={styles.actionButtonLabelHovered}>{t('Unfollow')}</span>*/}
{/* </>*/}
{/* }*/}
{/* >*/}
{/* <Icon name="author-unsubscribe" class={stylesButton.icon} />*/}
{/* </Show>*/}
{/* }*/}
{/* onClick={handleFollowClick}*/}
{/* isSubscribeButton={true}*/}
{/* class={clsx(styles.actionButton, {*/}
{/* [styles.iconed]: props.iconButtons,*/}
{/* [stylesButton.subscribed]: isSubscribed(),*/}
{/* })}*/}
{/* />*/}
{/* </Show>*/}
{/*</Show>*/}
<Show when={props.showMessageButton}> <Show when={props.showMessageButton}>
<Button <Button
variant={props.iconButtons ? 'secondary' : 'bordered'} variant={props.iconButtons ? 'secondary' : 'bordered'}

View File

@ -34,17 +34,18 @@ export const AuthorCard = (props: Props) => {
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 [subscriptionFilter, setSubscriptionFilter] = createSignal<SubscriptionFilter>('all')
const [isSubscribed, setIsSubscribed] = createSignal<boolean>()
const isProfileOwner = createMemo(() => author()?.slug === props.author.slug) const isProfileOwner = createMemo(() => author()?.slug === props.author.slug)
const { follow, isOwnerSubscribed, subscribeInAction } = useFollowing() const { follow, unfollow, subscriptions, subscribeInAction } = useFollowing()
onMount(() => { onMount(() => {
setAuthorSubs(props.following) setAuthorSubs(props.following)
}) })
const isSubscribed = isOwnerSubscribed(props.author?.id)
createEffect(() => { createEffect(() => {
console.log('%c!!! isSubscribed:', 'color: #bada55', isSubscribed()) if(!subscriptions || !props.author) return
const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.author?.id);
setIsSubscribed(subscribed)
}) })
const name = createMemo(() => { const name = createMemo(() => {
@ -83,18 +84,13 @@ export const AuthorCard = (props: Props) => {
} }
}) })
createEffect(() => {
console.log("!!! subscribeInAction:", subscribeInAction());
})
const handleFollowClick = () => { const handleFollowClick = () => {
console.log("!!! handleFollowClick:");
requireAuthentication(() => { requireAuthentication(() => {
follow(FollowingEntity.Author, props.author.slug) isSubscribed() ? unfollow(FollowingEntity.Author, props.author.slug) : follow(FollowingEntity.Author, props.author.slug)
}, 'subscribe') }, 'subscribe')
} }
const followButtonText = createMemo(() => { const followButtonText = createMemo(() => {
console.log("!!! subscribeInAction()", subscribeInAction());
if (subscribeInAction()?.slug === props.author.slug) { if (subscribeInAction()?.slug === props.author.slug) {
return subscribeInAction().type === 'subscribe' ? t('Subscribing...') : t('Unsubscribing...') return subscribeInAction().type === 'subscribe' ? t('Subscribing...') : t('Unsubscribing...')
} }
@ -256,13 +252,7 @@ export const AuthorCard = (props: Props) => {
<div class="col-24"> <div class="col-24">
<For each={props.followers}> <For each={props.followers}>
{(follower: Author) => ( {(follower: Author) => (
<AuthorBadge <AuthorBadge author={follower}/>
author={follower}
isFollowed={{
loaded: Boolean(authorSubs()),
value: isSubscribed(),
}}
/>
)} )}
</For> </For>
</div> </div>
@ -306,21 +296,9 @@ export const AuthorCard = (props: Props) => {
<For each={authorSubs()}> <For each={authorSubs()}>
{(subscription) => {(subscription) =>
isAuthor(subscription) ? ( isAuthor(subscription) ? (
<AuthorBadge <AuthorBadge author={subscription} />
isFollowed={{
loaded: Boolean(authorSubs()),
value: isSubscribed(),
}}
author={subscription}
/>
) : ( ) : (
<TopicBadge <TopicBadge topic={subscription}/>
isFollowed={{
loaded: Boolean(authorSubs()),
value: isSubscribed(),
}}
topic={subscription}
/>
) )
} }
</For> </For>

View File

@ -10,14 +10,12 @@ import { capitalize } from '../../../utils/capitalize'
import { getImageUrl } from '../../../utils/getImageUrl' import { getImageUrl } from '../../../utils/getImageUrl'
import { Button } from '../../_shared/Button' import { Button } from '../../_shared/Button'
import { CheckButton } from '../../_shared/CheckButton' import { CheckButton } from '../../_shared/CheckButton'
import { FollowedInfo } from '../../../pages/types'
import styles from './TopicBadge.module.scss' import styles from './TopicBadge.module.scss'
import { BadgeSubscribeButton } from "../../_shared/BadgeSubscribeButton";
type Props = { type Props = {
topic: Topic topic: Topic
minimizeSubscribeButton?: boolean minimizeSubscribeButton?: boolean
isFollowed?: FollowedInfo
showStat?: boolean showStat?: boolean
} }
@ -27,13 +25,18 @@ export const TopicBadge = (props: Props) => {
const [isMobileView, setIsMobileView] = createSignal(false) const [isMobileView, setIsMobileView] = createSignal(false)
const { requireAuthentication } = useSession() const { requireAuthentication } = useSession()
const { setFollowing, loading: subLoading } = useFollowing() const { setFollowing, loading: subLoading } = useFollowing()
const [isFollowed, setIsFollowed] = createSignal<boolean>() const [isSubscribed, setIsSubscribed] = createSignal<boolean>()
const { follow, unfollow, subscriptions, subscribeInAction } = useFollowing()
createEffect(() => {
if(!subscriptions || !props.topic) return
const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.topic?.id);
setIsSubscribed(subscribed)
})
const handleFollowClick = () => { const handleFollowClick = () => {
const value = !isFollowed()
requireAuthentication(() => { requireAuthentication(() => {
setIsFollowed(value) follow(FollowingEntity.Topic, props.topic.slug)
setFollowing(FollowingEntity.Topic, props.topic.slug, value)
}, 'subscribe') }, 'subscribe')
} }
@ -41,15 +44,6 @@ export const TopicBadge = (props: Props) => {
setIsMobileView(!mediaMatches.sm) setIsMobileView(!mediaMatches.sm)
}) })
createEffect(
on(
() => props.isFollowed,
() => {
setIsFollowed(props.isFollowed.value)
},
),
)
const title = () => const title = () =>
lang() === 'en' ? capitalize(props.topic.slug.replaceAll('-', ' ')) : props.topic.title lang() === 'en' ? capitalize(props.topic.slug.replaceAll('-', ' ')) : props.topic.title
@ -83,35 +77,8 @@ export const TopicBadge = (props: Props) => {
</Show> </Show>
</a> </a>
</div> </div>
<div class={styles.actions}> <div class={styles.actions}>
<Show <BadgeSubscribeButton isSubscribed={isSubscribed()} action={handleFollowClick}/>
when={!props.minimizeSubscribeButton}
fallback={
<CheckButton text={t('Follow')} checked={Boolean(isFollowed())} onClick={handleFollowClick} />
}
>
<Show
when={isFollowed()}
fallback={
<Button
variant="primary"
size="S"
value={subLoading() ? t('subscribing...') : t('Subscribe')}
onClick={handleFollowClick}
class={styles.subscribeButton}
/>
}
>
<Button
onClick={handleFollowClick}
variant="bordered"
size="S"
value={t('Following')}
class={styles.subscribeButton}
/>
</Show>
</Show>
</div> </div>
</div> </div>
<div class={styles.stats}> <div class={styles.stats}>

View File

@ -0,0 +1,29 @@
.actionButton {
border-radius: 0.8rem !important;
margin-right: 0 !important;
width: 9em;
&.iconed {
padding: 6px !important;
min-width: 4rem;
width: unset;
&:hover img {
filter: invert(1);
}
}
&:hover {
.actionButtonLabel {
display: none;
}
.actionButtonLabelHovered {
display: block;
}
}
}
.actionButtonLabelHovered {
display: none;
}

View File

@ -0,0 +1,74 @@
import { clsx } from 'clsx'
import styles from './BadgeDubscribeButton.module.scss'
import { CheckButton } from "../CheckButton";
import { Show } from "solid-js";
import { Button } from "../Button";
import { Icon } from "../Icon";
import stylesButton from "../Button/Button.module.scss";
import { useLocalize } from "../../../context/localize";
type Props = {
class?: string
isSubscribed: boolean
minimizeSubscribeButton?: boolean
action: () => void
iconButtons?: boolean
}
export const BadgeSubscribeButton = (props: Props) => {
const { t } = useLocalize()
return (
<div class={props.class}>
<Show
when={!props.minimizeSubscribeButton}
fallback={<CheckButton text={t('Follow')} checked={props.isSubscribed} onClick={props.action} />}
>
<Show
when={props.isSubscribed}
fallback={
<Button
variant={props.iconButtons ? 'secondary' : 'bordered'}
size="S"
value={
<Show when={props.iconButtons} fallback={t('Subscribe')}>
<Icon name="author-subscribe" class={stylesButton.icon} />
</Show>
}
onClick={props.action}
isSubscribeButton={true}
class={clsx(styles.actionButton, {
[styles.iconed]: props.iconButtons,
[stylesButton.subscribed]: props.isSubscribed,
})}
/>
}
>
<Button
variant={props.iconButtons ? 'secondary' : 'bordered'}
size="S"
value={
<Show
when={props.iconButtons}
fallback={
<>
<span class={styles.actionButtonLabel}>{t('Following')}</span>
<span class={styles.actionButtonLabelHovered}>{t('Unfollow')}</span>
</>
}
>
<Icon name="author-unsubscribe" class={stylesButton.icon} />
</Show>
}
onClick={props.action}
isSubscribeButton={true}
class={clsx(styles.actionButton, {
[styles.iconed]: props.iconButtons,
[stylesButton.subscribed]: props.isSubscribed,
})}
/>
</Show>
</Show>
</div>
)
}

View File

@ -0,0 +1 @@
export { BadgeSubscribeButton } from './BadgeSubscribeButton'

View File

@ -22,7 +22,6 @@ interface FollowingContextType {
loadSubscriptions: () => void loadSubscriptions: () => 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>
isOwnerSubscribed: (id: number | string) => Accessor<boolean>
// followers: Accessor<Author[]> // followers: Accessor<Author[]>
subscribeInAction?: Accessor<SubscribeAction> subscribeInAction?: Accessor<SubscribeAction>
} }
@ -50,10 +49,7 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
try { try {
if (apiClient.private) { if (apiClient.private) {
console.debug('[context.following] fetching subs data...') console.debug('[context.following] fetching subs data...')
console.log("%c!!! session()?.user.id:", 'background: #222; color: #bada55', session());
const result = await apiClient.getAuthorFollows({ user: session()?.user.id }) const result = await apiClient.getAuthorFollows({ user: session()?.user.id })
console.log("!!! result:", result);
setSubscriptions(result || EMPTY_SUBSCRIPTIONS) setSubscriptions(result || EMPTY_SUBSCRIPTIONS)
} }
} catch (error) { } catch (error) {
@ -99,11 +95,10 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
createEffect(() => { createEffect(() => {
// console.log("!!! cone setSubscribeInAction:", subscribeInAction()); if (author()) {
// if (author()) { console.debug('[context.following] author update detect')
// console.debug('[context.following] author update detect') fetchData()
// fetchData() }
// }
}) })
const setFollowing = (what: FollowingEntity, slug: string, value = true) => { const setFollowing = (what: FollowingEntity, slug: string, value = true) => {
@ -127,19 +122,14 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
} }
} }
const isOwnerSubscribed = (id?: number | string) => createMemo(() => { createEffect(() => {
console.log('%c!!! WTF:', 'color: #bada55', subscriptions); console.log('%c!!! WTF subscriptions:', 'color: #bada55', subscriptions);
if (!author() || !subscriptions) return false;
const isAuthorSubscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === id);
const isTopicSubscribed = subscriptions.topics?.some((topicEntity) => topicEntity.slug === id);
return !!isAuthorSubscribed || !!isTopicSubscribed;
}) })
const value: FollowingContextType = { const value: FollowingContextType = {
loading, loading,
subscriptions, subscriptions,
setSubscriptions, setSubscriptions,
isOwnerSubscribed,
setFollowing, setFollowing,
loadSubscriptions: fetchData, loadSubscriptions: fetchData,
follow, follow,

View File

@ -53,9 +53,4 @@ export type UploadedFile = {
originalFilename?: string originalFilename?: string
} }
export type FollowedInfo = {
value?: boolean
loaded?: boolean
}
export type SubscriptionFilter = 'all' | 'authors' | 'topics' | 'communities' export type SubscriptionFilter = 'all' | 'authors' | 'topics' | 'communities'