diff --git a/src/components/Author/Card.tsx b/src/components/Author/Card.tsx index feb02cb4..0f75c65d 100644 --- a/src/components/Author/Card.tsx +++ b/src/components/Author/Card.tsx @@ -9,6 +9,7 @@ import { locale } from '../../stores/ui' import { follow, unfollow } from '../../stores/zine/common' import { clsx } from 'clsx' import { useSession } from '../../context/session' +import { StatMetrics } from '../_shared/StatMetrics' interface AuthorCardProps { caption?: string @@ -69,9 +70,11 @@ export const AuthorCard = (props: AuthorCardProps) => { {props.isAuthorsList} -
- {bio()} -
+
diff --git a/src/components/Topic/Card.tsx b/src/components/Topic/Card.tsx index 8067171e..6cdc394a 100644 --- a/src/components/Topic/Card.tsx +++ b/src/components/Topic/Card.tsx @@ -1,14 +1,14 @@ -import { capitalize, plural } from '../../utils' +import { capitalize } from '../../utils' import styles from './Card.module.scss' import { createMemo, Show } from 'solid-js' import type { Topic } from '../../graphql/types.gen' import { FollowingEntity } from '../../graphql/types.gen' import { t } from '../../utils/intl' -import { locale } from '../../stores/ui' import { follow, unfollow } from '../../stores/zine/common' import { getLogger } from '../../utils/logger' import { clsx } from 'clsx' import { useSession } from '../../context/session' +import { StatMetrics } from '../_shared/StatMetrics' const log = getLogger('TopicCard') @@ -74,54 +74,6 @@ export const TopicCard = (props: TopicProps) => { {props.topic.body} - - -
- - - {props.topic.stat?.shouts + - ' ' + - t('post') + - plural( - props.topic.stat?.shouts || 0, - locale() === 'ru' ? ['ов', '', 'а'] : ['s', '', 's'] - )} - - - - - {props.topic.stat?.authors + - ' ' + - t('author') + - plural( - props.topic.stat?.authors || 0, - locale() === 'ru' ? ['ов', '', 'а'] : ['s', '', 's'] - )} - - - {props.topic.stat?.followers + - ' ' + - t('follower') + - plural( - props.topic.stat?.followers || 0, - locale() === 'ru' ? ['ов', '', 'а'] : ['s', '', 's'] - )} - - {/*FIXME*/} - {/**/} - {/* */} - {/* {topic().stat?.viewed +*/} - {/* ' ' +*/} - {/* t('view') +*/} - {/* plural(*/} - {/* topic().stat?.viewed || 0,*/} - {/* locale() === 'ru' ? ['ов', '', 'а'] : ['s', '', 's']*/} - {/* )}*/} - {/* */} - {/**/} - -
-
{ - const { sortedAuthors } = useAuthorsStore({ authors: props.authors }) const [limit, setLimit] = createSignal(PAGE_SIZE) + const { searchParams, changeSearchParam } = useRouter() + const { sortedAuthors } = useAuthorsStore({ + authors: props.authors, + sortBy: searchParams().by || 'shouts' + }) const { session } = useSession() + onMount(() => changeSearchParam('by', 'shouts')) createEffect(() => { setAuthorsSort(searchParams().by || 'shouts') + setLimit(PAGE_SIZE) }) const subscribed = (s) => Boolean(session()?.news?.authors && session()?.news?.authors?.includes(s || '')) - const { searchParams, changeSearchParam } = useRouter() - const byLetter = createMemo<{ [letter: string]: Author[] }>(() => { return sortedAuthors().reduce((acc, author) => { let letter = author.name.trim().split(' ').pop().at(0).toUpperCase() - if (!/[А-Я]/i.test(letter) && locale() === 'ru') letter = '@' + if (!/[А-я]/i.test(letter) && locale() === 'ru') letter = '@' if (!acc[letter]) acc[letter] = [] acc[letter].push(author) return acc @@ -64,8 +69,8 @@ export const AllAuthorsView = (props: Props) => {
  • {t('By shouts')}
  • -
  • - {t('By rating')} +
  • + {t('By rating')}
  • {t('By name')} @@ -167,15 +172,18 @@ export const AllAuthorsView = (props: Props) => { 0}> {(author) => ( - + <> + + + )} @@ -185,15 +193,18 @@ export const AllAuthorsView = (props: Props) => {
    {(author) => ( - + <> + + + )}
    diff --git a/src/components/Views/AllTopics.tsx b/src/components/Views/AllTopics.tsx index a28c71da..d6bce589 100644 --- a/src/components/Views/AllTopics.tsx +++ b/src/components/Views/AllTopics.tsx @@ -1,4 +1,4 @@ -import { createEffect, createMemo, createSignal, For, Show } from 'solid-js' +import { createEffect, createMemo, createSignal, For, onMount, Show } from 'solid-js' import type { Topic } from '../../graphql/types.gen' import { t } from '../../utils/intl' import { setTopicsSort, useTopicsStore } from '../../stores/zine/topics' @@ -11,6 +11,7 @@ import { translit } from '../../utils/ru2en' import styles from '../../styles/AllTopics.module.scss' import { SearchField } from '../_shared/SearchField' import { scrollHandler } from '../../utils/scroll' +import { StatMetrics } from '../_shared/StatMetrics' type AllTopicsPageSearchParams = { by: 'shouts' | 'authors' | 'title' | '' @@ -34,6 +35,7 @@ export const AllTopicsView = (props: AllTopicsViewProps) => { const { session } = useSession() + onMount(() => changeSearchParam('by', 'shouts')) createEffect(() => { setTopicsSort(searchParams().by || 'shouts') setLimit(PAGE_SIZE) @@ -182,12 +184,15 @@ export const AllTopicsView = (props: AllTopicsViewProps) => { {(topic) => ( - + <> + + + )} diff --git a/src/components/_shared/Stat.module.scss b/src/components/_shared/Stat.module.scss new file mode 100644 index 00000000..183bea64 --- /dev/null +++ b/src/components/_shared/Stat.module.scss @@ -0,0 +1,34 @@ +.statMetrics { + @include font-size(1.7rem); + + color: #9fa1a7; + display: flex; + margin-bottom: 1em; + + @include media-breakpoint-down(md) { + flex-wrap: wrap; + } +} + +.statMetricsItem { + @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; + } +} diff --git a/src/components/_shared/StatMetrics.tsx b/src/components/_shared/StatMetrics.tsx new file mode 100644 index 00000000..fd60946c --- /dev/null +++ b/src/components/_shared/StatMetrics.tsx @@ -0,0 +1,35 @@ +import { For } from 'solid-js' +import type { Stat, TopicStat } from '../../graphql/types.gen' +import { locale } from '../../stores/ui' +import { plural } from '../../utils' +import { t } from '../../utils/intl' +import styles from './Stat.module.scss' + +interface StatMetricsProps { + fields?: string[] + stat: Stat | TopicStat + compact?: boolean +} + +const pseudonames = { + comments: 'discussions' +} + +const nos = (s) => s.slice(0, s.length - 1) + +export const StatMetrics = (props: StatMetricsProps) => { + return ( +
    + + {(entity: string) => ( + + {props.stat[entity] + + ' ' + + t(nos(pseudonames[entity] || entity)) + + plural(props.stat[entity] || 0, locale() === 'ru' ? ['ов', '', 'а'] : ['s', '', 's'])} + + )} + +
    + ) +} diff --git a/src/graphql/query/authors-all.ts b/src/graphql/query/authors-all.ts index dd9ff818..1a1813ba 100644 --- a/src/graphql/query/authors-all.ts +++ b/src/graphql/query/authors-all.ts @@ -13,6 +13,7 @@ export default gql` stat { shouts followers + comments: commented } } } diff --git a/src/graphql/types.gen.ts b/src/graphql/types.gen.ts index a57bc253..e199e4cf 100644 --- a/src/graphql/types.gen.ts +++ b/src/graphql/types.gen.ts @@ -35,10 +35,10 @@ export type Author = { } export type AuthorStat = { + rating?: Maybe commented?: Maybe followers?: Maybe followings?: Maybe - rating?: Maybe shouts?: Maybe } @@ -615,6 +615,8 @@ export type Stat = { rating?: Maybe reacted?: Maybe viewed?: Maybe + shouts?: Maybe + followers?: Maybe } export type Subscription = { diff --git a/src/locales/ru.json b/src/locales/ru.json index dce9e981..d65efc8b 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -178,5 +178,7 @@ "topics": "темы", "user already exist": "пользователь уже существует", "view": "просмотр", - "zine": "журнал" + "zine": "журнал", + "shout": "пост", + "discussion": "дискурс" } diff --git a/src/stores/zine/authors.ts b/src/stores/zine/authors.ts index 49d8b53c..c133b8ec 100644 --- a/src/stores/zine/authors.ts +++ b/src/stores/zine/authors.ts @@ -2,8 +2,9 @@ import { apiClient } from '../../utils/apiClient' import type { Author } from '../../graphql/types.gen' import { createSignal } from 'solid-js' import { createLazyMemo } from '@solid-primitives/memo' +import { byStat } from '../../utils/sortby' -export type AuthorsSortBy = 'shouts' | 'name' | 'rating' +export type AuthorsSortBy = 'shouts' | 'name' | 'followers' const [sortAllBy, setSortAllBy] = createSignal('shouts') @@ -15,21 +16,15 @@ const [authorsByTopic, setAuthorsByTopic] = createSignal<{ [topicSlug: string]: const sortedAuthors = createLazyMemo(() => { const authors = Object.values(authorEntities()) switch (sortAllBy()) { - // case 'created': { - // log.debug('sorted by created') - // authors.sort(byCreated) - // break - // } - case 'rating': { - // TODO: + case 'followers': { + authors.sort(byStat('followers')) break } case 'shouts': { - // TODO: + authors.sort(byStat('shouts')) break } case 'name': { - console.debug('sorted by name') authors.sort((a, b) => a.name.localeCompare(b.name)) break } @@ -84,9 +79,13 @@ export const loadAllAuthors = async (): Promise => { type InitialState = { authors?: Author[] + sortBy?: AuthorsSortBy } export const useAuthorsStore = (initialState: InitialState = {}) => { + if (initialState.sortBy) { + setSortAllBy(initialState.sortBy) + } addAuthors([...(initialState.authors || [])]) return { authorEntities, sortedAuthors, authorsByTopic } diff --git a/src/utils/apiClient.ts b/src/utils/apiClient.ts index f263cb3d..7ea0c301 100644 --- a/src/utils/apiClient.ts +++ b/src/utils/apiClient.ts @@ -260,9 +260,7 @@ export const apiClient = { }, getReactionsBy: async ({ by, limit = REACTIONS_AMOUNT_PER_PAGE, offset = 0 }) => { const resp = await publicGraphQLClient.query(reactionsLoadBy, { by, limit, offset }).toPromise() - - console.log('resactions response', resp) - + resp.error ?? console.error(resp.error) return resp.data.loadReactionsBy }, diff --git a/src/utils/sortby.ts b/src/utils/sortby.ts index 9d001037..4b03fdaee 100644 --- a/src/utils/sortby.ts +++ b/src/utils/sortby.ts @@ -27,7 +27,6 @@ export const byLength = ( return 0 } -// TODO more typing export const byStat = (metric: keyof Stat) => { return (a, b) => { const x = (a?.stat && a.stat[metric]) || 0