From 80ffc564a98d7a57114517c1a413d036a558cfd1 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Mon, 11 Mar 2024 08:20:00 +0300 Subject: [PATCH 01/27] [WiP] --- .../Author/AuthorCard/AuthorCard.tsx | 16 +++++++-- src/context/following.tsx | 35 ++++++++++++++++--- src/graphql/client/core.ts | 1 + 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index 75f23639..6b67215a 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -36,13 +36,14 @@ export const AuthorCard = (props: Props) => { const [subscriptionFilter, setSubscriptionFilter] = createSignal('all') const [isFollowed, setIsFollowed] = createSignal() const isProfileOwner = createMemo(() => author()?.slug === props.author.slug) - const { setFollowing, isOwnerSubscribed } = useFollowing() + const { follow, isOwnerSubscribed, subscribeInAction } = useFollowing() onMount(() => { setAuthorSubs(props.following) }) createEffect(() => { + console.log("!!! isOwnerSubscribed(props.author?.id):", isOwnerSubscribed(props.author?.id)); setIsFollowed(isOwnerSubscribed(props.author?.id)) }) @@ -82,15 +83,24 @@ export const AuthorCard = (props: Props) => { } }) + createEffect(() => { + console.log("!!! subscribeInAction:", subscribeInAction()); + }) const handleFollowClick = () => { + console.log("!!! handleFollowClick:"); const value = !isFollowed() requireAuthentication(() => { setIsFollowed(value) - setFollowing(FollowingEntity.Author, props.author.slug, value) + follow(FollowingEntity.Author, props.author.slug) }, 'subscribe') } const followButtonText = createMemo(() => { + console.log("!!! subscribeInAction()", subscribeInAction()); + if (subscribeInAction()?.slug === props.author.slug) { + return subscribeInAction().type === 'subscribe' ? t('Subscribing...') : t('Unsubscribing...') + } + if (isOwnerSubscribed(props.author?.id)) { return ( <> @@ -200,7 +210,7 @@ export const AuthorCard = (props: Props) => { when={isProfileOwner()} fallback={
- +
- } - > - - - - } - onClick={handleFollowClick} - isSubscribeButton={true} - class={clsx(styles.actionButton, { - [styles.iconed]: props.iconButtons, - [stylesButton.subscribed]: isFollowed(), - })} - /> - } - > -
-
- - } - > - - } - > -
diff --git a/src/components/_shared/BadgeSubscribeButton/BadgeDubscribeButton.module.scss b/src/components/_shared/BadgeSubscribeButton/BadgeDubscribeButton.module.scss new file mode 100644 index 00000000..b5a7480f --- /dev/null +++ b/src/components/_shared/BadgeSubscribeButton/BadgeDubscribeButton.module.scss @@ -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; +} diff --git a/src/components/_shared/BadgeSubscribeButton/BadgeSubscribeButton.tsx b/src/components/_shared/BadgeSubscribeButton/BadgeSubscribeButton.tsx new file mode 100644 index 00000000..d69e65f5 --- /dev/null +++ b/src/components/_shared/BadgeSubscribeButton/BadgeSubscribeButton.tsx @@ -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 ( +
+ } + > + + + + } + onClick={props.action} + isSubscribeButton={true} + class={clsx(styles.actionButton, { + [styles.iconed]: props.iconButtons, + [stylesButton.subscribed]: props.isSubscribed, + })} + /> + } + > +
+ ) +} diff --git a/src/components/_shared/BadgeSubscribeButton/index.ts b/src/components/_shared/BadgeSubscribeButton/index.ts new file mode 100644 index 00000000..b359ecff --- /dev/null +++ b/src/components/_shared/BadgeSubscribeButton/index.ts @@ -0,0 +1 @@ +export { BadgeSubscribeButton } from './BadgeSubscribeButton' diff --git a/src/context/following.tsx b/src/context/following.tsx index 3dd41c37..8edb6d1a 100644 --- a/src/context/following.tsx +++ b/src/context/following.tsx @@ -22,7 +22,6 @@ interface FollowingContextType { loadSubscriptions: () => void follow: (what: FollowingEntity, slug: string) => Promise unfollow: (what: FollowingEntity, slug: string) => Promise - isOwnerSubscribed: (id: number | string) => Accessor // followers: Accessor subscribeInAction?: Accessor } @@ -50,10 +49,7 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { try { if (apiClient.private) { 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 }) - console.log("!!! result:", result); setSubscriptions(result || EMPTY_SUBSCRIPTIONS) } } catch (error) { @@ -99,11 +95,10 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { createEffect(() => { - // console.log("!!! cone setSubscribeInAction:", subscribeInAction()); - // if (author()) { - // console.debug('[context.following] author update detect') - // fetchData() - // } + if (author()) { + console.debug('[context.following] author update detect') + fetchData() + } }) 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(() => { - console.log('%c!!! WTF:', '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; + createEffect(() => { + console.log('%c!!! WTF subscriptions:', 'color: #bada55', subscriptions); }) const value: FollowingContextType = { loading, subscriptions, setSubscriptions, - isOwnerSubscribed, setFollowing, loadSubscriptions: fetchData, follow, diff --git a/src/pages/types.ts b/src/pages/types.ts index af588d9f..dc4d7132 100644 --- a/src/pages/types.ts +++ b/src/pages/types.ts @@ -53,9 +53,4 @@ export type UploadedFile = { originalFilename?: string } -export type FollowedInfo = { - value?: boolean - loaded?: boolean -} - export type SubscriptionFilter = 'all' | 'authors' | 'topics' | 'communities' From edf4400627abf3bd6a092a068b56872204c9f7f0 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Fri, 15 Mar 2024 17:55:37 +0300 Subject: [PATCH 05/27] Change follow logic --- .../AuthorBadge/AuthorBadge.module.scss | 26 +++++++++ .../Author/AuthorBadge/AuthorBadge.tsx | 51 +---------------- .../Author/AuthorCard/AuthorCard.tsx | 8 ++- .../Topic/TopicBadge/TopicBadge.tsx | 9 ++- src/components/Views/Author/Author.tsx | 57 +++++++++---------- .../ProfileSubscriptions.tsx | 2 - .../BadgeSubscribeButton.tsx | 53 ++++++++++------- src/context/following.tsx | 4 -- src/graphql/client/core.ts | 1 - 9 files changed, 100 insertions(+), 111 deletions(-) diff --git a/src/components/Author/AuthorBadge/AuthorBadge.module.scss b/src/components/Author/AuthorBadge/AuthorBadge.module.scss index 3c3b1366..ebd5d145 100644 --- a/src/components/Author/AuthorBadge/AuthorBadge.module.scss +++ b/src/components/Author/AuthorBadge/AuthorBadge.module.scss @@ -89,4 +89,30 @@ 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; + } + } + } } diff --git a/src/components/Author/AuthorBadge/AuthorBadge.tsx b/src/components/Author/AuthorBadge/AuthorBadge.tsx index 3342b5b8..6f0020ae 100644 --- a/src/components/Author/AuthorBadge/AuthorBadge.tsx +++ b/src/components/Author/AuthorBadge/AuthorBadge.tsx @@ -15,7 +15,6 @@ import { CheckButton } from '../../_shared/CheckButton' import { ConditionalWrapper } from '../../_shared/ConditionalWrapper' import { Icon } from '../../_shared/Icon' import { Userpic } from '../Userpic' -import stylesButton from '../../_shared/Button/Button.module.scss' import styles from './AuthorBadge.module.scss' import { BadgeSubscribeButton } from "../../_shared/BadgeSubscribeButton"; @@ -131,56 +130,8 @@ export const AuthorBadge = (props: Props) => { handleFollowClick()} isSubscribed={isSubscribed()} + actionMessageType={subscribeInAction()?.slug === props.author.slug ? subscribeInAction().type : undefined} /> - {/*}*/} - {/*>*/} - {/* */} - {/* */} - {/* */} - {/* }*/} - {/* onClick={handleFollowClick}*/} - {/* isSubscribeButton={true}*/} - {/* class={clsx(styles.actionButton, {*/} - {/* [styles.iconed]: props.iconButtons,*/} - {/* [stylesButton.subscribed]: isSubscribed(),*/} - {/* })}*/} - {/* />*/} - {/* }*/} - {/* >*/} - {/* */} - {/* {t('Following')}*/} - {/* {t('Unfollow')}*/} - {/* */} - {/* }*/} - {/* >*/} - {/* */} - {/* */} - {/* }*/} - {/* onClick={handleFollowClick}*/} - {/* isSubscribeButton={true}*/} - {/* class={clsx(styles.actionButton, {*/} - {/* [styles.iconed]: props.iconButtons,*/} - {/* [stylesButton.subscribed]: isSubscribed(),*/} - {/* })}*/} - {/* />*/} - {/* */} - {/**/}
- +
diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index d4d37fde..9558cef9 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -3,7 +3,7 @@ import type { Author, Reaction, Shout, Topic } from '../../../graphql/schema/cor import { getPagePath } from '@nanostores/router' import { Meta, Title } from '@solidjs/meta' import { clsx } from 'clsx' -import { For, Match, Show, Switch, createEffect, createMemo, createSignal, onMount } from 'solid-js' +import { For, Match, Show, Switch, createEffect, createMemo, createSignal, onMount, on } from "solid-js"; import { useFollowing } from '../../../context/following' import { useLocalize } from '../../../context/localize' @@ -48,13 +48,11 @@ export const AuthorView = (props: Props) => { const [commented, setCommented] = createSignal() // current author - console.log('%c!!! :', 'color: #bada55', props.author) - const [author, setAuthor] = createSignal(props.author) + // const [author, _] = createSignal(props.author) createEffect(async () => { - if (author()?.id && !author().stat) { - const a = await loadAuthor({ slug: '', author_id: author().id }) - console.log('%c!!! A2:', 'color: #bada55', props.author) + if (props.author?.id && !props.author.stat) { + const a = await loadAuthor({ slug: '', author_id: props.author.id }) console.debug('[AuthorView] loaded author:', a) } }) @@ -63,14 +61,11 @@ export const AuthorView = (props: Props) => { const bioWrapperRef: { current: HTMLDivElement } = { current: null } const fetchData = async (author: Author) => { - console.log('%c!!! slug:', 'color: #bada55', author) try { const [subscriptionsResult, followersResult] = await Promise.all([ apiClient.getAuthorFollows({ author_id: author.id }), apiClient.getAuthorFollowers({ slug: author.slug }), ]) - console.log('%c!!! subscriptionsResult:', 'color: #bada55', subscriptionsResult) - console.log('%c!!! followersResult:', 'color: #bada55', followersResult) const { authors, topics } = subscriptionsResult setFollowing([...(authors || []), ...(topics || [])]) setFollowers(followersResult || []) @@ -99,7 +94,6 @@ export const AuthorView = (props: Props) => { } onMount(() => { - fetchData(author()) checkBioHeight() // pagination if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) { @@ -118,41 +112,46 @@ export const AuthorView = (props: Props) => { setCommented(data) } - createEffect(() => { - if (author()) { - fetchComments(author()) - } - }) + createEffect( + on( + () => props.author, + () => { + fetchData(props.author) + fetchComments(props.author) + }, + { defer: true }, + ), + ) const ogImage = createMemo(() => - author()?.pic - ? getImageUrl(author()?.pic, { width: 1200 }) + props.author?.pic + ? getImageUrl(props.author?.pic, { width: 1200 }) : getImageUrl('production/image/logo_image.png'), ) - const description = createMemo(() => getDescription(author()?.bio)) + const description = createMemo(() => getDescription(props.author?.bio)) const handleDeleteComment = (id: number) => { setCommented((prev) => prev.filter((comment) => comment.id !== id)) } return (
- - {author().name} + + {props.author.name} - + - +
- }> + }> <>
- +
@@ -161,16 +160,16 @@ export const AuthorView = (props: Props) => { {t('Publications')} - - {author().stat.shouts} + + {props.author.stat.shouts}
  • {t('Comments')} - - {author().stat.comments} + + {props.author.stat.comments}
  • @@ -206,7 +205,7 @@ export const AuthorView = (props: Props) => { class={styles.longBio} classList={{ [styles.longBioExpanded]: isBioExpanded() }} > -
    (bioContainerRef.current = el)} innerHTML={author().about} /> +
    (bioContainerRef.current = el)} innerHTML={props.author.about} />
    diff --git a/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx b/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx index 82e2af19..6e1b4d8d 100644 --- a/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx +++ b/src/components/Views/ProfileSubscriptions/ProfileSubscriptions.tsx @@ -27,8 +27,6 @@ export const ProfileSubscriptions = () => { const [searchQuery, setSearchQuery] = createSignal('') const fetchSubscriptions = async () => { - - console.log('%c!!! :', 'color: #bada55', 'Профайл fetchSubscriptions' ) try { const slug = author()?.slug const authorFollows = await apiClient.getAuthorFollows({ slug }) diff --git a/src/components/_shared/BadgeSubscribeButton/BadgeSubscribeButton.tsx b/src/components/_shared/BadgeSubscribeButton/BadgeSubscribeButton.tsx index d69e65f5..902f9e07 100644 --- a/src/components/_shared/BadgeSubscribeButton/BadgeSubscribeButton.tsx +++ b/src/components/_shared/BadgeSubscribeButton/BadgeSubscribeButton.tsx @@ -1,37 +1,46 @@ -import { clsx } from 'clsx' -import styles from './BadgeDubscribeButton.module.scss' +import { clsx } from "clsx"; +import styles from "./BadgeDubscribeButton.module.scss"; import { CheckButton } from "../CheckButton"; -import { Show } from "solid-js"; +import { createMemo, Show } from "solid-js"; import { Button } from "../Button"; import { Icon } from "../Icon"; import stylesButton from "../Button/Button.module.scss"; import { useLocalize } from "../../../context/localize"; +import { useFollowing } from "../../../context/following"; type Props = { - class?: string - isSubscribed: boolean - minimizeSubscribeButton?: boolean - action: () => void - iconButtons?: boolean -} + class?: string; + isSubscribed: boolean; + minimizeSubscribeButton?: boolean; + action: () => void; + iconButtons?: boolean; + actionMessageType?: "subscribe" | "unsubscribe"; +}; export const BadgeSubscribeButton = (props: Props) => { - const { t } = useLocalize() + const { t } = useLocalize(); + + const inActionText = createMemo(() => { + return props.actionMessageType === "subscribe" ? t("Subscribing...") : t("Unsubscribing..."); + }); return (
    } + fallback={} > + } @@ -45,16 +54,20 @@ export const BadgeSubscribeButton = (props: Props) => { } >
    - ) -} + ); +}; diff --git a/src/context/following.tsx b/src/context/following.tsx index 8edb6d1a..4dd9c4be 100644 --- a/src/context/following.tsx +++ b/src/context/following.tsx @@ -122,10 +122,6 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { } } - createEffect(() => { - console.log('%c!!! WTF subscriptions:', 'color: #bada55', subscriptions); - }) - const value: FollowingContextType = { loading, subscriptions, diff --git a/src/graphql/client/core.ts b/src/graphql/client/core.ts index b9c77ab6..4cbe81c2 100644 --- a/src/graphql/client/core.ts +++ b/src/graphql/client/core.ts @@ -92,7 +92,6 @@ export const apiClient = { follow: async ({ what, slug }: { what: FollowingEntity; slug: string }) => { const response = await apiClient.private.mutation(followMutation, { what, slug }).toPromise() - console.log("!!! response FOLLOW AAAA:", 'background: #222; color: #bada55', response); return response.data.follow }, From d202845aab1ef62703f053d0378ba57a625a3690 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Fri, 15 Mar 2024 17:57:03 +0300 Subject: [PATCH 06/27] run fix checks --- .../Author/AuthorBadge/AuthorBadge.tsx | 14 +++-- .../Author/AuthorCard/AuthorCard.tsx | 24 ++++----- .../Topic/TopicBadge/TopicBadge.tsx | 14 +++-- src/components/Views/Author/Author.tsx | 2 +- .../BadgeSubscribeButton.tsx | 54 +++++++++---------- src/context/following.tsx | 26 +++++---- 6 files changed, 67 insertions(+), 67 deletions(-) diff --git a/src/components/Author/AuthorBadge/AuthorBadge.tsx b/src/components/Author/AuthorBadge/AuthorBadge.tsx index 6f0020ae..bc7c3bbd 100644 --- a/src/components/Author/AuthorBadge/AuthorBadge.tsx +++ b/src/components/Author/AuthorBadge/AuthorBadge.tsx @@ -10,13 +10,13 @@ import { Author, FollowingEntity } from '../../../graphql/schema/core.gen' import { router, useRouter } from '../../../stores/router' import { translit } from '../../../utils/ru2en' import { isCyrillic } from '../../../utils/translate' +import { BadgeSubscribeButton } from '../../_shared/BadgeSubscribeButton' import { Button } from '../../_shared/Button' import { CheckButton } from '../../_shared/CheckButton' import { ConditionalWrapper } from '../../_shared/ConditionalWrapper' import { Icon } from '../../_shared/Icon' import { Userpic } from '../Userpic' import styles from './AuthorBadge.module.scss' -import { BadgeSubscribeButton } from "../../_shared/BadgeSubscribeButton"; type Props = { author: Author @@ -36,8 +36,8 @@ export const AuthorBadge = (props: Props) => { const [isSubscribed, setIsSubscribed] = createSignal() createEffect(() => { - if(!subscriptions || !props.author) return - const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.author?.id); + if (!subscriptions || !props.author) return + const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.author?.id) setIsSubscribed(subscribed) }) @@ -73,7 +73,9 @@ export const AuthorBadge = (props: Props) => { const handleFollowClick = () => { requireAuthentication(() => { - isSubscribed() ? unfollow(FollowingEntity.Author, props.author.slug) : follow(FollowingEntity.Author, props.author.slug) + isSubscribed() + ? unfollow(FollowingEntity.Author, props.author.slug) + : follow(FollowingEntity.Author, props.author.slug) }, 'subscribe') } @@ -130,7 +132,9 @@ export const AuthorBadge = (props: Props) => { handleFollowClick()} isSubscribed={isSubscribed()} - actionMessageType={subscribeInAction()?.slug === props.author.slug ? subscribeInAction().type : undefined} + actionMessageType={ + subscribeInAction()?.slug === props.author.slug ? subscribeInAction().type : undefined + } />
    - ); -}; + ) +} diff --git a/src/context/following.tsx b/src/context/following.tsx index 4dd9c4be..2e2d1a44 100644 --- a/src/context/following.tsx +++ b/src/context/following.tsx @@ -1,8 +1,8 @@ -import { Accessor, JSX, createContext, createEffect, createSignal, useContext, createMemo } from "solid-js"; +import { Accessor, JSX, createContext, createEffect, createMemo, createSignal, useContext } from 'solid-js' import { createStore } from 'solid-js/store' import { apiClient } from '../graphql/client/core' -import { Author, AuthorFollows, Community, FollowingEntity, Topic } from "../graphql/schema/core.gen"; +import { Author, AuthorFollows, Community, FollowingEntity, Topic } from '../graphql/schema/core.gen' import { useSession } from './session' @@ -60,24 +60,23 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { } createEffect(() => { - console.info('[context.following] subs:', subscriptions); + console.info('[context.following] subs:', subscriptions) }) - const follow = async (what: FollowingEntity, slug: string) => { - if (!author()) return; - setSubscribeInAction({ slug, type: 'subscribe' }); + if (!author()) return + setSubscribeInAction({ slug, type: 'subscribe' }) try { - const subscriptionData = await apiClient.follow({ what, slug }); + const subscriptionData = await apiClient.follow({ what, slug }) setSubscriptions((prevSubscriptions) => { - if (!prevSubscriptions[what]) prevSubscriptions[what] = []; - prevSubscriptions[what].push(subscriptionData); - return prevSubscriptions; - }); + if (!prevSubscriptions[what]) prevSubscriptions[what] = [] + prevSubscriptions[what].push(subscriptionData) + return prevSubscriptions + }) } catch (error) { - console.error(error); + console.error(error) } finally { - setSubscribeInAction(); // Сбрасываем состояние действия подписки. + setSubscribeInAction() // Сбрасываем состояние действия подписки. } } @@ -93,7 +92,6 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { } } - createEffect(() => { if (author()) { console.debug('[context.following] author update detect') From 00a043683510a42bf52df311d9da78c496371b58 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Fri, 15 Mar 2024 17:58:34 +0300 Subject: [PATCH 07/27] cleanup code --- src/components/AuthorsList/AuthorsList.tsx | 9 +-------- src/components/Feed/Beside.tsx | 8 +------- src/components/Views/AllTopics/AllTopics.tsx | 6 ------ 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/components/AuthorsList/AuthorsList.tsx b/src/components/AuthorsList/AuthorsList.tsx index ccde9a92..ab48bae5 100644 --- a/src/components/AuthorsList/AuthorsList.tsx +++ b/src/components/AuthorsList/AuthorsList.tsx @@ -21,7 +21,6 @@ const PAGE_SIZE = 20 export const AuthorsList = (props: Props) => { const { t } = useLocalize() - const { isOwnerSubscribed } = useFollowing() const { authorsByShouts, authorsByFollowers } = useAuthorsStore() const [loading, setLoading] = createSignal(false) const [currentPage, setCurrentPage] = createSignal({ shouts: 0, followers: 0 }) @@ -83,13 +82,7 @@ export const AuthorsList = (props: Props) => { {(author) => (
    - +
    )} diff --git a/src/components/Feed/Beside.tsx b/src/components/Feed/Beside.tsx index 87dc462e..06a3c3e2 100644 --- a/src/components/Feed/Beside.tsx +++ b/src/components/Feed/Beside.tsx @@ -30,7 +30,6 @@ type Props = { export const Beside = (props: Props) => { const { t } = useLocalize() - const { isOwnerSubscribed } = useFollowing() return ( 0}> @@ -86,12 +85,7 @@ export const Beside = (props: Props) => { /> - + { return keys }) - const { isOwnerSubscribed } = useFollowing() - const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE) const [searchQuery, setSearchQuery] = createSignal('') const filteredResults = createMemo(() => { @@ -190,10 +188,6 @@ export const AllTopics = (props: Props) => { <> 0, - value: isOwnerSubscribed(topic.slug), - }} showStat={true} /> From dd4065036f23153fe31df0de9bafb7c975fab871 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Fri, 15 Mar 2024 18:24:33 +0300 Subject: [PATCH 08/27] cleanup code --- src/components/AuthorsList/AuthorsList.tsx | 2 +- src/components/Feed/Beside.tsx | 2 +- src/components/Topic/Card.tsx | 28 +++++++++++-------- .../Topic/TopicBadge/TopicBadge.tsx | 4 +-- src/components/Views/AllTopics/AllTopics.tsx | 5 +--- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/components/AuthorsList/AuthorsList.tsx b/src/components/AuthorsList/AuthorsList.tsx index ab48bae5..e4e014ae 100644 --- a/src/components/AuthorsList/AuthorsList.tsx +++ b/src/components/AuthorsList/AuthorsList.tsx @@ -82,7 +82,7 @@ export const AuthorsList = (props: Props) => { {(author) => (
    - +
    )} diff --git a/src/components/Feed/Beside.tsx b/src/components/Feed/Beside.tsx index 06a3c3e2..2d3a7caa 100644 --- a/src/components/Feed/Beside.tsx +++ b/src/components/Feed/Beside.tsx @@ -85,7 +85,7 @@ export const Beside = (props: Props) => { />
    - + { capitalize(lang() === 'en' ? props.topic.slug.replaceAll('-', ' ') : props.topic.title || ''), ) const { author, requireAuthentication } = useSession() - const { setFollowing, loading: subLoading } = useFollowing() - const [followed, setFollowed] = createSignal() + const [isSubscribed, setIsSubscribed] = createSignal() + const { follow, unfollow, subscriptions, subscribeInAction } = useFollowing() + + createEffect(() => { + if (!subscriptions || !props.topic) return + const subscribed = subscriptions.topics?.some((topics) => topics.id === props.topic?.id) + setIsSubscribed(subscribed) + }) const handleFollowClick = () => { - const value = !followed() requireAuthentication(() => { - setFollowed(value) - setFollowing(FollowingEntity.Topic, props.topic.slug, value) + follow(FollowingEntity.Topic, props.topic.slug) }, 'subscribe') } @@ -53,12 +57,12 @@ export const TopicCard = (props: TopicProps) => { return ( <> - + - + {t('Unfollow')} {t('Following')} @@ -130,7 +134,7 @@ export const TopicCard = (props: TopicProps) => { fallback={ } @@ -142,10 +146,10 @@ export const TopicCard = (props: TopicProps) => { onClick={handleFollowClick} isSubscribeButton={true} class={clsx(styles.actionButton, { - [styles.isSubscribing]: subLoading(), - [stylesButton.subscribed]: followed(), + [styles.isSubscribing]: + subscribeInAction()?.slug === props.topic.slug ? subscribeInAction().type : undefined, + [stylesButton.subscribed]: isSubscribed(), })} - // disabled={subLoading()} /> diff --git a/src/components/Topic/TopicBadge/TopicBadge.tsx b/src/components/Topic/TopicBadge/TopicBadge.tsx index fd292abc..47a27dcb 100644 --- a/src/components/Topic/TopicBadge/TopicBadge.tsx +++ b/src/components/Topic/TopicBadge/TopicBadge.tsx @@ -9,8 +9,6 @@ import { FollowingEntity, Topic } from '../../../graphql/schema/core.gen' import { capitalize } from '../../../utils/capitalize' import { getImageUrl } from '../../../utils/getImageUrl' import { BadgeSubscribeButton } from '../../_shared/BadgeSubscribeButton' -import { Button } from '../../_shared/Button' -import { CheckButton } from '../../_shared/CheckButton' import styles from './TopicBadge.module.scss' type Props = { @@ -29,7 +27,7 @@ export const TopicBadge = (props: Props) => { createEffect(() => { if (!subscriptions || !props.topic) return - const subscribed = subscriptions.authors?.some((authorEntity) => authorEntity.id === props.topic?.id) + const subscribed = subscriptions.topics?.some((topics) => topics.id === props.topic?.id) setIsSubscribed(subscribed) }) diff --git a/src/components/Views/AllTopics/AllTopics.tsx b/src/components/Views/AllTopics/AllTopics.tsx index 636b9310..20e14709 100644 --- a/src/components/Views/AllTopics/AllTopics.tsx +++ b/src/components/Views/AllTopics/AllTopics.tsx @@ -186,10 +186,7 @@ export const AllTopics = (props: Props) => { {(topic) => ( <> - + )} From 8d78ba2c627306ade18f34d616862d3b90ca03c1 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Fri, 15 Mar 2024 19:42:55 +0300 Subject: [PATCH 09/27] fix preload author --- src/components/Views/Author/Author.tsx | 62 ++++++++++++++------------ 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index b5c82c0e..6ca91c66 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -48,11 +48,19 @@ export const AuthorView = (props: Props) => { const [commented, setCommented] = createSignal() // current author - // const [author, _] = createSignal(props.author) + const [author, setAuthor] = createSignal() + createEffect(() => { + try { + const a = authorEntities()[props.authorSlug] + setAuthor(a) + } catch (error) { + console.debug(error) + } + }) createEffect(async () => { - if (props.author?.id && !props.author.stat) { - const a = await loadAuthor({ slug: '', author_id: props.author.id }) + if (author()?.id && !author().stat) { + const a = await loadAuthor({ slug: '', author_id: author().id }) console.debug('[AuthorView] loaded author:', a) } }) @@ -112,46 +120,42 @@ export const AuthorView = (props: Props) => { setCommented(data) } - createEffect( - on( - () => props.author, - () => { - fetchData(props.author) - fetchComments(props.author) - }, - { defer: true }, - ), - ) + createEffect(() => { + if (author()) { + fetchData(author()) + fetchComments(author()) + } + }) const ogImage = createMemo(() => - props.author?.pic - ? getImageUrl(props.author?.pic, { width: 1200 }) + author()?.pic + ? getImageUrl(author()?.pic, { width: 1200 }) : getImageUrl('production/image/logo_image.png'), ) - const description = createMemo(() => getDescription(props.author?.bio)) + const description = createMemo(() => getDescription(author()?.bio)) const handleDeleteComment = (id: number) => { setCommented((prev) => prev.filter((comment) => comment.id !== id)) } return (
  • {t('Comments')} - - {props.author.stat.comments} + + {author().stat.comments}
  • @@ -183,10 +187,10 @@ export const AuthorView = (props: Props) => {
  • - +
    {t('All posts rating')} - +
    @@ -205,7 +209,7 @@ export const AuthorView = (props: Props) => { class={styles.longBio} classList={{ [styles.longBioExpanded]: isBioExpanded() }} > -
    (bioContainerRef.current = el)} innerHTML={props.author.about} /> +
    (bioContainerRef.current = el)} innerHTML={author().about} />
    From c80b2f044ac8c2953af1d3cacf490de5445f3a72 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Thu, 21 Mar 2024 15:48:54 +0300 Subject: [PATCH 10/27] Merge dev --- src/components/Views/Author/Author.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index a592f433..e0fd82e8 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -23,10 +23,10 @@ import { Row2 } from '../../Feed/Row2' import { Row3 } from '../../Feed/Row3' import { Loading } from '../../_shared/Loading' +import { MODALS, hideModal } from '../../../stores/ui' import { byCreated } from '../../../utils/sortby' import stylesArticle from '../../Article/Article.module.scss' import styles from './Author.module.scss' -import { hideModal, MODALS } from "../../../stores/ui"; type Props = { shouts: Shout[] From cca858dadf8358147026b14d632d868ab5121261 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Mon, 22 Apr 2024 16:37:47 +0300 Subject: [PATCH 11/27] fix image width --- src/styles/app.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/app.scss b/src/styles/app.scss index 7afcb070..0bb124a4 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -588,6 +588,7 @@ figure { display: block; max-height: 90vh; margin: auto; + width: 100%; } } From 36fff73af6d93054f3c6f9b7efb6ac0ccaf157b5 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:47:37 +0300 Subject: [PATCH 12/27] Hotfix/remove state params after login (#443) * hide autofill in profilr settings * fix Image width * Remove state search params after login * fix biome --- src/components/Article/Article.module.scss | 1 + src/components/Nav/HeaderAuth.tsx | 16 +++++++++------- .../ProfileSettings/ProfileSettings.tsx | 2 ++ src/context/session.tsx | 7 ++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss index ad85c0a3..55b8dd95 100644 --- a/src/components/Article/Article.module.scss +++ b/src/components/Article/Article.module.scss @@ -22,6 +22,7 @@ img { .articleContent { img:not([data-disable-lightbox='true']) { cursor: zoom-in; + width: 100%; } } diff --git a/src/components/Nav/HeaderAuth.tsx b/src/components/Nav/HeaderAuth.tsx index 8ddff9df..7f439b94 100644 --- a/src/components/Nav/HeaderAuth.tsx +++ b/src/components/Nav/HeaderAuth.tsx @@ -227,13 +227,15 @@ export const HeaderAuth = (props: Props) => { - - {t('Enter')} - - {/**/} - -
    + + + } > diff --git a/src/components/ProfileSettings/ProfileSettings.tsx b/src/components/ProfileSettings/ProfileSettings.tsx index 90a23b33..2e393710 100644 --- a/src/components/ProfileSettings/ProfileSettings.tsx +++ b/src/components/ProfileSettings/ProfileSettings.tsx @@ -264,6 +264,7 @@ export const ProfileSettings = () => { type="text" name="username" id="username" + autocomplete="one-time-code" placeholder={t('Name')} onInput={(event) => updateFormField('name', event.currentTarget.value)} value={form.name} @@ -289,6 +290,7 @@ export const ProfileSettings = () => { type="text" name="user-address" id="user-address" + autocomplete="one-time-code2" onInput={(event) => updateFormField('slug', event.currentTarget.value)} value={form.slug} ref={(el) => (slugInputRef.current = el)} diff --git a/src/context/session.tsx b/src/context/session.tsx index fa068bc8..3a189cc1 100644 --- a/src/context/session.tsx +++ b/src/context/session.tsx @@ -1,5 +1,5 @@ import type { Accessor, JSX, Resource } from 'solid-js' -import type { AuthModalSource } from '../components/Nav/AuthModal/types' +import type { AuthModalSearchParams, AuthModalSource } from '../components/Nav/AuthModal/types' import type { Author } from '../graphql/schema/core.gen' import { @@ -29,7 +29,6 @@ import { import { inboxClient } from '../graphql/client/chat' import { apiClient } from '../graphql/client/core' -import { notifierClient } from '../graphql/client/notifier' import { useRouter } from '../stores/router' import { showModal } from '../stores/ui' import { addAuthors } from '../stores/zine/authors' @@ -136,6 +135,7 @@ export const SessionProvider = (props: { const [isSessionLoaded, setIsSessionLoaded] = createSignal(false) const [authError, setAuthError] = createSignal('') + const { clearSearchParams } = useRouter() // Function to load session data const sessionData = async () => { @@ -143,7 +143,7 @@ export const SessionProvider = (props: { const s: ApiResponse = await authorizer().getSession() if (s?.data) { console.info('[context.session] loading session', s) - + clearSearchParams() // Set session expiration time in local storage const expires_at = new Date(Date.now() + s.data.expires_in * 1000) localStorage.setItem('expires_at', `${expires_at.getTime()}`) @@ -379,6 +379,7 @@ export const SessionProvider = (props: { } const isAuthenticated = createMemo(() => Boolean(author())) + const actions = { loadSession, requireAuthentication, From c9f494d2c19e60e802c6402d8ad124f504067541 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:59:39 +0300 Subject: [PATCH 13/27] Hide autofill in profile settings (#442) * hide autofill in profile settings --- src/components/ProfileSettings/ProfileSettings.tsx | 10 ++++++---- src/pages/profile/Settings.module.scss | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/components/ProfileSettings/ProfileSettings.tsx b/src/components/ProfileSettings/ProfileSettings.tsx index 2e393710..9d405a9b 100644 --- a/src/components/ProfileSettings/ProfileSettings.tsx +++ b/src/components/ProfileSettings/ProfileSettings.tsx @@ -195,7 +195,7 @@ export const ProfileSettings = () => {

    {t('Profile settings')}

    {t('Here you can customize your profile the way you want.')}

    -
    +

    {t('Userpic')}

    {
    updateFormField('name', event.currentTarget.value)} value={form.name} ref={(el) => (nameInputRef.current = el)} /> - +
    { type="text" name="user-address" id="user-address" + data-lpignore="true" autocomplete="one-time-code2" onInput={(event) => updateFormField('slug', event.currentTarget.value)} value={form.slug} diff --git a/src/pages/profile/Settings.module.scss b/src/pages/profile/Settings.module.scss index 898b2496..9bd4906c 100644 --- a/src/pages/profile/Settings.module.scss +++ b/src/pages/profile/Settings.module.scss @@ -320,3 +320,14 @@ h5 { margin-bottom: 0; } } + +// disable last pass extention + +div[data-lastpass-icon-root="true"] { + opacity: 0 !important; +} + +div[data-lastpass-infield="true"] { + opacity: 0 !important; +} + From 437e666b3c4ea83627f1658014ac83e4adeac088 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Wed, 24 Apr 2024 09:40:37 +0300 Subject: [PATCH 14/27] getImageUrl.ts refactoring --- src/utils/getImageUrl.ts | 53 +++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/utils/getImageUrl.ts b/src/utils/getImageUrl.ts index bc3c9073..e4bd09f6 100644 --- a/src/utils/getImageUrl.ts +++ b/src/utils/getImageUrl.ts @@ -1,31 +1,41 @@ import { cdnUrl, thumborUrl } from './config' -const getSizeUrlPart = (options: { width?: number; height?: number; noSizeUrlPart?: boolean } = {}) => { - const widthString = options.width ? options.width.toString() : '' - const heightString = options.height ? options.height.toString() : '' +const URL_CONFIG = { + cdnUrl: cdnUrl, + thumborUrl: thumborUrl, + audioSubfolder: 'audio', + imageSubfolder: 'image', + productionFolder: 'production/', +} - if (!(widthString || heightString) || options.noSizeUrlPart) { - return '' - } +const AUDIO_EXTENSIONS = new Set(['wav', 'mp3', 'ogg', 'aif', 'flac']) - return `${widthString}x${heightString}/` +const isAudioFile = (filename: string): boolean => { + const extension = filename.split('.').pop()?.toLowerCase() + return AUDIO_EXTENSIONS.has(extension ?? '') +} +const getLastSegment = (url: string): string => url.toLowerCase().split('/').pop() || '' + +const buildSizePart = (width?: number, height?: number, includeSize = true): string => { + if (!includeSize) return '' + const widthPart = width ? width.toString() : '' + const heightPart = height ? height.toString() : '' + return widthPart || heightPart ? `${widthPart}x${heightPart}/` : '' } export const getImageUrl = ( src: string, options: { width?: number; height?: number; noSizeUrlPart?: boolean } = {}, -) => { +): string => { if (!src.includes('discours.io') && src.includes('http')) { return src } - const filename = src.toLowerCase().split('/').pop() - const ext = filename.split('.').pop() - const isAudio = ext in ['wav', 'mp3', 'ogg', 'aif', 'flac'] - const base = isAudio ? cdnUrl : `${thumborUrl}/unsafe/` - const suffix = isAudio || options.noSizeUrlPart ? '' : getSizeUrlPart(options) - const subfolder = isAudio ? 'audio' : 'image' + const filename = getLastSegment(src) + const base = isAudioFile(filename) ? URL_CONFIG.cdnUrl : URL_CONFIG.thumborUrl + const suffix = options.noSizeUrlPart ? '' : buildSizePart(options.width, options.height) + const subfolder = isAudioFile(filename) ? URL_CONFIG.audioSubfolder : URL_CONFIG.imageSubfolder - return `${base}${suffix}production/${subfolder}/${filename}` + return `${base}${suffix}${URL_CONFIG.productionFolder}${subfolder}/${filename}` } export const getOpenGraphImageUrl = ( @@ -37,17 +47,16 @@ export const getOpenGraphImageUrl = ( width?: number height?: number }, -) => { - const sizeUrlPart = getSizeUrlPart(options) - +): string => { + const sizeUrlPart = buildSizePart(options.width, options.height) const filtersPart = `filters:discourstext('${encodeURIComponent(options.topic)}','${encodeURIComponent( options.author, )}','${encodeURIComponent(options.title)}')/` - if (src.startsWith(thumborUrl)) { - const thumborKey = src.replace(`${thumborUrl}/unsafe`, '') - return `${thumborUrl}/unsafe/${sizeUrlPart}${filtersPart}${thumborKey}` + if (src.startsWith(URL_CONFIG.thumborUrl)) { + const thumborKey = src.replace(URL_CONFIG.thumborUrl, '') + return `${URL_CONFIG.thumborUrl}${sizeUrlPart}${filtersPart}${thumborKey}` } - return `${thumborUrl}/unsafe/${sizeUrlPart}${filtersPart}${src}` + return `${URL_CONFIG.thumborUrl}${sizeUrlPart}${filtersPart}${src}` } From 59561ec26b9b631873b317fa80b9d8dfa828f442 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Wed, 24 Apr 2024 10:22:05 +0300 Subject: [PATCH 15/27] add unsafe --- src/utils/getImageUrl.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/getImageUrl.ts b/src/utils/getImageUrl.ts index e4bd09f6..d820bfae 100644 --- a/src/utils/getImageUrl.ts +++ b/src/utils/getImageUrl.ts @@ -2,11 +2,11 @@ import { cdnUrl, thumborUrl } from './config' const URL_CONFIG = { cdnUrl: cdnUrl, - thumborUrl: thumborUrl, - audioSubfolder: 'audio', - imageSubfolder: 'image', - productionFolder: 'production/', -} + thumborUrl: `${thumborUrl}/unsafe/`, + audioSubfolder: "audio", + imageSubfolder: "image", + productionFolder: "production/", +}; const AUDIO_EXTENSIONS = new Set(['wav', 'mp3', 'ogg', 'aif', 'flac']) From fc056f14b853900bad49c8190d4a74047cf621cb Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Wed, 24 Apr 2024 10:23:42 +0300 Subject: [PATCH 16/27] fix biome --- src/utils/getImageUrl.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/getImageUrl.ts b/src/utils/getImageUrl.ts index d820bfae..e13872cc 100644 --- a/src/utils/getImageUrl.ts +++ b/src/utils/getImageUrl.ts @@ -3,10 +3,10 @@ import { cdnUrl, thumborUrl } from './config' const URL_CONFIG = { cdnUrl: cdnUrl, thumborUrl: `${thumborUrl}/unsafe/`, - audioSubfolder: "audio", - imageSubfolder: "image", - productionFolder: "production/", -}; + audioSubfolder: 'audio', + imageSubfolder: 'image', + productionFolder: 'production/', +} const AUDIO_EXTENSIONS = new Set(['wav', 'mp3', 'ogg', 'aif', 'flac']) From 4a7a052d67683f7c215577c02b7800bcfaea1efa Mon Sep 17 00:00:00 2001 From: Arkadzi Rakouski Date: Wed, 24 Apr 2024 11:07:07 +0300 Subject: [PATCH 17/27] fix falsy active header (#444) fix falsy active header --- src/components/TableOfContents/TableOfContents.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TableOfContents/TableOfContents.tsx b/src/components/TableOfContents/TableOfContents.tsx index f7000e20..322aa925 100644 --- a/src/components/TableOfContents/TableOfContents.tsx +++ b/src/components/TableOfContents/TableOfContents.tsx @@ -17,7 +17,7 @@ interface Props { const isInViewport = (el: Element): boolean => { const rect = el.getBoundingClientRect() - return rect.top <= DEFAULT_HEADER_OFFSET + return rect.top <= DEFAULT_HEADER_OFFSET + 24 // default offset + 1.5em (default header margin-top) } const scrollToHeader = (element) => { window.scrollTo({ From c1d1f05edf8fc6f268fde48337849b8facd86b60 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:08:00 +0300 Subject: [PATCH 18/27] Hotfix/author comments render (#446) Fix comments fetching --- src/components/Views/Author/Author.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index ad3840d9..4ddb5649 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -128,7 +128,7 @@ export const AuthorView = (props: Props) => { const fetchComments = async (commenter: Author) => { const data = await apiClient.getReactionsBy({ - by: { comment: false, created_by: commenter.id }, + by: { comment: true, created_by: commenter.id }, }) setCommented(data) } From 9028618067db28d552b392ccf5b4d38cfb4f5c57 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:30:17 +0300 Subject: [PATCH 19/27] Add gallery description (#434) Add gallery description --- src/components/Article/FullArticle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 6d9a670f..bc7fd403 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -330,7 +330,7 @@ export const FullArticle = (props: Props) => { width: 1200, }) - const description = getDescription(props.article.description || body()) + const description = getDescription(props.article.description || body() || media()[0]?.body) const ogTitle = props.article.title const keywords = getKeywords(props.article) const shareUrl = getShareUrl({ pathname: `/${props.article.slug}` }) From 5334291878f34995d5f6b1b06f11502f28aa6b7a Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Wed, 24 Apr 2024 15:48:19 +0300 Subject: [PATCH 20/27] fix feed dropdown --- src/components/Views/Feed/Feed.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/Views/Feed/Feed.tsx b/src/components/Views/Feed/Feed.tsx index aeee25b9..e457cafc 100644 --- a/src/components/Views/Feed/Feed.tsx +++ b/src/components/Views/Feed/Feed.tsx @@ -54,6 +54,13 @@ type FeedSearchParams = { visibility: VisibilityMode } +type Props = { + loadShouts: (options: LoadShoutsOptions) => Promise<{ + hasMore: boolean + newShouts: Shout[] + }> +} + const getFromDate = (period: FeedPeriod): number => { const now = new Date() let d: Date = now @@ -74,18 +81,10 @@ const getFromDate = (period: FeedPeriod): number => { return Math.floor(d.getTime() / 1000) } -type Props = { - loadShouts: (options: LoadShoutsOptions) => Promise<{ - hasMore: boolean - newShouts: Shout[] - }> -} - export const FeedView = (props: Props) => { const { t } = useLocalize() const monthPeriod: PeriodItem = { value: 'month', title: t('This month') } - const visibilityAll = { value: 'featured', title: t('All') } const periods: PeriodItem[] = [ { value: 'week', title: t('This week') }, @@ -121,7 +120,7 @@ export const FeedView = (props: Props) => { const currentVisibility = createMemo(() => { const visibility = visibilities.find((v) => v.value === searchParams().visibility) if (!visibility) { - return visibilityAll + return visibilities[0] } return visibility }) @@ -172,6 +171,7 @@ export const FeedView = (props: Props) => { } const visibilityMode = searchParams().visibility + if (visibilityMode === 'all') { options.filters = { ...options.filters } } else if (visibilityMode) { @@ -185,6 +185,7 @@ export const FeedView = (props: Props) => { const period = searchParams().period || 'month' options.filters = { after: getFromDate(period) } } + return props.loadShouts(options) } From 23326b10f77403d956b28ca7cfa1b66b5029cdb4 Mon Sep 17 00:00:00 2001 From: Untone Date: Thu, 25 Apr 2024 13:53:51 +0300 Subject: [PATCH 21/27] postmerge-fix --- src/components/Views/Author/Author.tsx | 4 ++-- src/context/following.tsx | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index df63b945..ca2cdd0c 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -75,7 +75,7 @@ export const AuthorView = (props: Props) => { const bioContainerRef: { current: HTMLDivElement } = { current: null } const bioWrapperRef: { current: HTMLDivElement } = { current: null } - const fetchData = async (author: Author) => { + const fetchData = async (slug: string) => { try { const [subscriptionsResult, followersResult, authorResult] = await Promise.all([ apiClient.getAuthorFollows({ slug }), @@ -134,7 +134,7 @@ export const AuthorView = (props: Props) => { createEffect(() => { if (author()) { - fetchData(author()) + fetchData(author().slug) fetchComments(author()) } }) diff --git a/src/context/following.tsx b/src/context/following.tsx index a6b07a03..8fd07cba 100644 --- a/src/context/following.tsx +++ b/src/context/following.tsx @@ -2,7 +2,7 @@ import { Accessor, JSX, createContext, createEffect, createMemo, createSignal, u import { createStore } from 'solid-js/store' import { apiClient } from '../graphql/client/core' -import { Author, AuthorFollowsResult, FollowingEntity } from '../graphql/schema/core.gen' +import { Author, AuthorFollowsResult, FollowingEntity, Community, Topic } from '../graphql/schema/core.gen' import { useSession } from './session' @@ -64,6 +64,7 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { console.info('[context.following] subs:', subscriptions) }) + const [subscribeInAction, setSubscribeInAction] = createSignal() const follow = async (what: FollowingEntity, slug: string) => { if (!author()) return setSubscribeInAction({ slug, type: 'subscribe' }) From 84fb665f9db0489fdf0795157e3dad9d7c18883e Mon Sep 17 00:00:00 2001 From: Untone Date: Thu, 25 Apr 2024 13:55:58 +0300 Subject: [PATCH 22/27] postmerge-fix-2 --- src/context/following.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/following.tsx b/src/context/following.tsx index 8fd07cba..7859102b 100644 --- a/src/context/following.tsx +++ b/src/context/following.tsx @@ -2,7 +2,7 @@ import { Accessor, JSX, createContext, createEffect, createMemo, createSignal, u import { createStore } from 'solid-js/store' import { apiClient } from '../graphql/client/core' -import { Author, AuthorFollowsResult, FollowingEntity, Community, Topic } from '../graphql/schema/core.gen' +import { Author, AuthorFollowsResult, Community, FollowingEntity, Topic } from '../graphql/schema/core.gen' import { useSession } from './session' From 1c94638ce8fee4a7173e2a250d0da10af7353c1f Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:55:34 +0300 Subject: [PATCH 23/27] Add pagination on Expo (#441) * Add pagination on Expo * update Expo load articles method --- src/components/Views/Expo/Expo.tsx | 51 +++++++++++------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/components/Views/Expo/Expo.tsx b/src/components/Views/Expo/Expo.tsx index 82e43dfe..8506bf24 100644 --- a/src/components/Views/Expo/Expo.tsx +++ b/src/components/Views/Expo/Expo.tsx @@ -1,16 +1,14 @@ import { getPagePath } from '@nanostores/router' import { clsx } from 'clsx' -import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js' +import { For, Show, createEffect, createSignal, on, onCleanup, onMount } from 'solid-js' import { useLocalize } from '../../../context/localize' import { apiClient } from '../../../graphql/client/core' import { LoadShoutsFilters, LoadShoutsOptions, Shout } from '../../../graphql/schema/core.gen' import { LayoutType } from '../../../pages/types' import { router } from '../../../stores/router' -import { loadShouts, resetSortedArticles, useArticlesStore } from '../../../stores/zine/articles' import { getUnixtime } from '../../../utils/getServerDate' import { restoreScrollPosition, saveScrollPosition } from '../../../utils/scroll' -import { splitToPages } from '../../../utils/splitToPages' import { ArticleCard } from '../../Feed/ArticleCard' import { Button } from '../../_shared/Button' import { ConditionalWrapper } from '../../_shared/ConditionalWrapper' @@ -28,19 +26,12 @@ export const PRERENDERED_ARTICLES_COUNT = 36 const LOAD_MORE_PAGE_SIZE = 12 export const Expo = (props: Props) => { - const [isLoaded, setIsLoaded] = createSignal(Boolean(props.shouts)) + const { t } = useLocalize() const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) - const [favoriteTopArticles, setFavoriteTopArticles] = createSignal([]) const [reactedTopMonthArticles, setReactedTopMonthArticles] = createSignal([]) - - const { t } = useLocalize() - - const { sortedArticles } = useArticlesStore({ - shouts: isLoaded() ? props.shouts : [], - layout: props.layout, - }) - + const [articlesEndPage, setArticlesEndPage] = createSignal(PRERENDERED_ARTICLES_COUNT) + const [expoShouts, setExpoShouts] = createSignal([]) const getLoadShoutsFilters = (additionalFilters: LoadShoutsFilters = {}): LoadShoutsFilters => { const filters = { ...additionalFilters } @@ -58,15 +49,18 @@ export const Expo = (props: Props) => { const options: LoadShoutsOptions = { filters: getLoadShoutsFilters(), limit: count, - offset: sortedArticles().length, + offset: expoShouts().length, } options.filters = props.layout ? { layouts: [props.layout] } : { layouts: ['audio', 'video', 'image', 'literature'] } - const { hasMore } = await loadShouts(options) + const newShouts = await apiClient.getShouts(options) + const hasMore = newShouts?.length !== options.limit + 1 && newShouts?.length !== 0 setIsLoadMoreButtonVisible(hasMore) + + setExpoShouts((prev) => [...prev, ...newShouts]) } const loadMoreWithoutScrolling = async (count: number) => { @@ -100,19 +94,7 @@ export const Expo = (props: Props) => { } onMount(() => { - if (isLoaded()) { - return - } - loadMore(PRERENDERED_ARTICLES_COUNT + LOAD_MORE_PAGE_SIZE) - setIsLoaded(true) - }) - - onMount(() => { - if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) { - loadMore(LOAD_MORE_PAGE_SIZE) - } - loadRandomTopArticles() loadRandomTopMonthArticles() }) @@ -121,9 +103,11 @@ export const Expo = (props: Props) => { on( () => props.layout, () => { - resetSortedArticles() + setExpoShouts([]) + setIsLoadMoreButtonVisible(false) setFavoriteTopArticles([]) setReactedTopMonthArticles([]) + setArticlesEndPage(PRERENDERED_ARTICLES_COUNT) loadMore(PRERENDERED_ARTICLES_COUNT + LOAD_MORE_PAGE_SIZE) loadRandomTopArticles() loadRandomTopMonthArticles() @@ -132,16 +116,17 @@ export const Expo = (props: Props) => { ) onCleanup(() => { - resetSortedArticles() + setExpoShouts([]) }) const handleLoadMoreClick = () => { loadMoreWithoutScrolling(LOAD_MORE_PAGE_SIZE) + setArticlesEndPage((prev) => prev + LOAD_MORE_PAGE_SIZE) } return (
    - 0} fallback={}> + 0} fallback={}>
    • @@ -194,7 +179,7 @@ export const Expo = (props: Props) => {
    - + {(shout) => (
    { 0} keyed={true}> - + {(shout) => (
    { 0} keyed={true}> - + {(shout) => (
    Date: Fri, 26 Apr 2024 06:05:10 +0300 Subject: [PATCH 24/27] update --- src/components/Views/Author/Author.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index ca2cdd0c..61cf9130 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -113,12 +113,7 @@ export const AuthorView = (props: Props) => { onMount(() => { if (!modal) hideModal() checkBioHeight() - fetchData(props.authorSlug) - - // pagination - if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) { - loadMore() - } + loadMore() }) const pages = createMemo(() => @@ -132,12 +127,17 @@ export const AuthorView = (props: Props) => { setCommented(data) } - createEffect(() => { - if (author()) { - fetchData(author().slug) - fetchComments(author()) - } - }) + const authorSlug = createMemo(() => author()?.slug) + createEffect( + on( + () => authorSlug(), + (slug) => { + fetchData(slug) + fetchComments(author()) + }, + { defer: true }, + ), + ) const ogImage = createMemo(() => author()?.pic From b3155c453515e05c42640efdb334d02992289b51 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Fri, 26 Apr 2024 06:13:23 +0300 Subject: [PATCH 25/27] fix author view --- src/components/Views/Author/Author.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index 61cf9130..3c87d04b 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -112,6 +112,7 @@ export const AuthorView = (props: Props) => { onMount(() => { if (!modal) hideModal() + fetchData(props.authorSlug) checkBioHeight() loadMore() }) @@ -131,8 +132,8 @@ export const AuthorView = (props: Props) => { createEffect( on( () => authorSlug(), - (slug) => { - fetchData(slug) + () => { + fetchData(authorSlug()) fetchComments(author()) }, { defer: true }, From a4d6466392091b137cce1e23b0171989ed1dffc0 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Tue, 30 Apr 2024 18:58:27 +0300 Subject: [PATCH 26/27] Fixed scrolling to comments (#449) --- .../Article/Comment/Comment.module.scss | 4 +++ .../CommentDate/CommentDate.module.scss | 5 +--- src/components/Article/FullArticle.tsx | 28 +++++++++---------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/Article/Comment/Comment.module.scss b/src/components/Article/Comment/Comment.module.scss index 50170022..0191442c 100644 --- a/src/components/Article/Comment/Comment.module.scss +++ b/src/components/Article/Comment/Comment.module.scss @@ -179,6 +179,10 @@ @include font-size(1.2rem); } +.commentAuthor { + margin-right: 2rem; +} + .articleAuthor { @include font-size(1.2rem); diff --git a/src/components/Article/CommentDate/CommentDate.module.scss b/src/components/Article/CommentDate/CommentDate.module.scss index 50cf7d57..c648cca1 100644 --- a/src/components/Article/CommentDate/CommentDate.module.scss +++ b/src/components/Article/CommentDate/CommentDate.module.scss @@ -3,14 +3,11 @@ color: var(--secondary-color); display: flex; - align-items: flex-start; - justify-content: flex-start; + justify-content: center; flex-direction: column; - gap: .5rem; flex: 1; flex-wrap: wrap; font-size: 1.2rem; - margin-bottom: .5rem; .date { font-weight: 500; diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index bc7fd403..25001f38 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -60,7 +60,7 @@ export type ArticlePageSearchParams = { const scrollTo = (el: HTMLElement) => { const { top } = el.getBoundingClientRect() window.scrollTo({ - top: top - DEFAULT_HEADER_OFFSET, + top: top + window.scrollY - DEFAULT_HEADER_OFFSET, left: 0, behavior: 'smooth', }) @@ -152,19 +152,6 @@ export const FullArticle = (props: Props) => { current: HTMLDivElement } = { current: null } - createEffect(() => { - if (props.scrollToComments) { - scrollTo(commentsRef.current) - } - }) - - createEffect(() => { - if (searchParams()?.scrollTo === 'comments' && commentsRef.current) { - requestAnimationFrame(() => scrollTo(commentsRef.current)) - changeSearchParams({ scrollTo: null }) - } - }) - createEffect(() => { if (searchParams().commentId && isReactionsLoaded()) { const commentElement = document.querySelector( @@ -320,6 +307,19 @@ export const FullArticle = (props: Props) => { window?.addEventListener('resize', updateIframeSizes) onCleanup(() => window.removeEventListener('resize', updateIframeSizes)) + + createEffect(() => { + if (props.scrollToComments) { + scrollTo(commentsRef.current) + } + }) + + createEffect(() => { + if (searchParams()?.scrollTo === 'comments' && commentsRef.current) { + requestAnimationFrame(() => scrollTo(commentsRef.current)) + changeSearchParams({ scrollTo: null }) + } + }) }) const cover = props.article.cover ?? 'production/image/logo_image.png' From 04978ebc7c19e37c94800b478d90f56b714ebeef Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Tue, 30 Apr 2024 20:22:44 +0300 Subject: [PATCH 27/27] Feed style fixes (#450) * Feed style fixes * Change subscriptions list icon --- public/icons/feed-all.svg | 2 +- public/icons/toggle-arrow.svg | 3 +++ public/locales/ru/translation.json | 4 ++-- .../Feed/Sidebar/Sidebar.module.scss | 24 ++++++++++++------- src/components/Feed/Sidebar/Sidebar.tsx | 1 + src/components/Views/Feed/Feed.module.scss | 2 +- 6 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 public/icons/toggle-arrow.svg diff --git a/public/icons/feed-all.svg b/public/icons/feed-all.svg index 486d1409..629f6105 100644 --- a/public/icons/feed-all.svg +++ b/public/icons/feed-all.svg @@ -1,3 +1,3 @@ - + diff --git a/public/icons/toggle-arrow.svg b/public/icons/toggle-arrow.svg new file mode 100644 index 00000000..fb552529 --- /dev/null +++ b/public/icons/toggle-arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 13ba2de4..8df3deff 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -31,7 +31,7 @@ "All posts rating": "Рейтинг всех постов", "All posts": "Все публикации", "All topics": "Все темы", - "All": "Все", + "All": "Общая лента", "Almost done! Check your email.": "Почти готово! Осталось подтвердить вашу почту.", "Are you sure you want to delete this comment?": "Уверены, что хотите удалить этот комментарий?", "Are you sure you want to delete this draft?": "Уверены, что хотите удалить этот черновик?", @@ -156,7 +156,7 @@ "FAQ": "Советы и предложения", "Favorite topics": "Избранные темы", "Favorite": "Избранное", - "Feed settings": "Настройки ленты", + "Feed settings": "Настроить ленту", "Feed": "Лента", "Feedback": "Обратная связь", "Fill email": "Введите почту", diff --git a/src/components/Feed/Sidebar/Sidebar.module.scss b/src/components/Feed/Sidebar/Sidebar.module.scss index e6675249..6ae8bda9 100644 --- a/src/components/Feed/Sidebar/Sidebar.module.scss +++ b/src/components/Feed/Sidebar/Sidebar.module.scss @@ -84,6 +84,10 @@ @include media-breakpoint-down(sm) { right: 2px; } + + a { + border: none; + } } .settingsLabel { @@ -136,20 +140,22 @@ text-transform: uppercase; position: relative; - &::after { - content: '+'; - font-size: 1.6em; - line-height: 1; + .icon { + margin: 0; + min-width: 1.8rem; position: absolute; - right: 2.5rem; - top: -0.2em; + right: 1.7rem; + top: 50%; + transform: translateY(-50%) rotate(180deg); + transform-origin: center; transition: transform 0.3s; + width: 1.8rem; } &.opened { - &::after { - right: 0.9rem; - transform: rotate(45deg); + .icon { + right: 0; + transform: translateY(-50%); } } } diff --git a/src/components/Feed/Sidebar/Sidebar.tsx b/src/components/Feed/Sidebar/Sidebar.tsx index 9fed7c61..213589ef 100644 --- a/src/components/Feed/Sidebar/Sidebar.tsx +++ b/src/components/Feed/Sidebar/Sidebar.tsx @@ -119,6 +119,7 @@ export const Sidebar = () => { }} > {t('My subscriptions')} +
      diff --git a/src/components/Views/Feed/Feed.module.scss b/src/components/Views/Feed/Feed.module.scss index 27a3854b..3cb18604 100644 --- a/src/components/Views/Feed/Feed.module.scss +++ b/src/components/Views/Feed/Feed.module.scss @@ -170,7 +170,7 @@ } } -.commentArticleTitle { +.comment .commentArticleTitle { line-clamp: 1; -webkit-line-clamp: 1;