apifixes-wip

This commit is contained in:
tonyrewin 2022-09-13 11:05:11 +03:00
parent fe88d3e150
commit 4d1e7f7831
10 changed files with 1177 additions and 1181 deletions

View File

@ -1,5 +1,5 @@
{ {
"name": "discoursio-astro", "name": "discoursio-webapp",
"version": "0.5.1", "version": "0.5.1",
"private": true, "private": true,
"license": "MIT", "license": "MIT",
@ -28,7 +28,7 @@
"vercel-build": "astro build" "vercel-build": "astro build"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.159.0", "@aws-sdk/client-s3": "^3.169.0",
"@nanostores/i18n": "^0.6.0", "@nanostores/i18n": "^0.6.0",
"@nanostores/persistent": "^0.6.2", "@nanostores/persistent": "^0.6.2",
"@nanostores/router": "^0.6.0", "@nanostores/router": "^0.6.0",
@ -37,7 +37,7 @@
"google-translate-api-x": "^10.3.5", "google-translate-api-x": "^10.3.5",
"loglevel": "^1.8.0", "loglevel": "^1.8.0",
"loglevel-plugin-prefix": "^0.8.4", "loglevel-plugin-prefix": "^0.8.4",
"mailgun.js": "^8.0.0", "mailgun.js": "^8.0.1",
"nanostores": "^0.6.0", "nanostores": "^0.6.0",
"rehype-autolink-headings": "^6.1.1", "rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1", "rehype-slug": "^5.0.1",

View File

@ -1,16 +1,18 @@
import { Show, createMemo, createSignal, createEffect } from 'solid-js' import { Show, createMemo } from 'solid-js'
import type { Author, Shout, Topic } from '../../graphql/types.gen' import type { Author, Shout, Topic } from '../../graphql/types.gen'
import Row2 from '../Feed/Row2' import Row2 from '../Feed/Row2'
import Row3 from '../Feed/Row3' import Row3 from '../Feed/Row3'
import AuthorFull from '../Author/Full' import AuthorFull from '../Author/Full'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { useAuthorsStore } from '../../stores/zine/authors' import { useAuthorsStore } from '../../stores/zine/authors'
import { params as paramsStore } from '../../stores/router' import { params } from '../../stores/router'
import { useArticlesStore } from '../../stores/zine/articles' import { useArticlesStore } from '../../stores/zine/articles'
import '../../styles/Topic.scss' import '../../styles/Topic.scss'
import Beside from '../Feed/Beside' import Beside from '../Feed/Beside'
import { useStore } from '@nanostores/solid' import { useStore } from '@nanostores/solid'
import { useTopicsStore } from '../../stores/zine/topics'
import { unique } from '../../utils'
type AuthorProps = { type AuthorProps = {
authorArticles: Shout[] authorArticles: Shout[]
@ -18,44 +20,29 @@ type AuthorProps = {
} }
export const AuthorPage = (props: AuthorProps) => { export const AuthorPage = (props: AuthorProps) => {
const params = useStore(paramsStore) const args = useStore(params)
const { getSortedArticles: articles, getArticlesByAuthors: articlesByAuthors } = useArticlesStore({ const { getSortedArticles: articles, getArticlesByAuthors: articlesByAuthors } = useArticlesStore({
sortedArticles: props.authorArticles sortedArticles: props.authorArticles
}) })
const { getAuthorEntities: authors } = useAuthorsStore([props.author]) const { getAuthorEntities: authors } = useAuthorsStore([props.author])
const author = createMemo(() => authors()[props.author.slug]) const author = createMemo(() => authors()[props.author.slug])
const slug = createMemo(() => author().slug) const topics = createMemo(() => {
/* const ttt = []
const slug = createMemo<string>(() => { articlesByAuthors()[author().slug].forEach((s: Shout) =>
let slug = props?.slug s.topics.forEach((tpc: Topic) => ttt.push(tpc))
if (props?.slug.startsWith('@')) slug = slug.replace('@', '') )
return slug return unique(ttt)
}) })
*/ const { getSortedTopics } = useTopicsStore({ topics: topics() })
const [authorTopics, setAuthorTopics] = createSignal<Partial<Topic>[]>([])
createEffect(() => {
if (authorTopics().length === 0 && articles().length > 0) {
const r = [] as Topic[]
articlesByAuthors()[slug()].forEach((a: Shout) => {
a.topics.forEach((topic: Topic) => {
if (!r.some((t) => t.slug === topic.slug)) r.push(topic)
})
})
setAuthorTopics(r)
}
}, [articles()])
const title = createMemo(() => { const title = createMemo(() => {
const m = params()['by'] const m = args()['by']
if (m === 'viewed') return t('Top viewed') if (m === 'viewed') return t('Top viewed')
if (m === 'rating') return t('Top rated') if (m === 'rating') return t('Top rated')
if (m === 'commented') return t('Top discussed') if (m === 'commented') return t('Top discussed')
return t('Top recent') return t('Top recent')
}) })
const setBy = (what: string) => {
params()['by'] = what
}
return ( return (
<div class="container author-page"> <div class="container author-page">
<Show when={author()} fallback={<div class="center">{t('Loading')}</div>}> <Show when={author()} fallback={<div class="center">{t('Loading')}</div>}>
@ -63,23 +50,23 @@ export const AuthorPage = (props: AuthorProps) => {
<div class="row group__controls"> <div class="row group__controls">
<div class="col-md-8"> <div class="col-md-8">
<ul class="view-switcher"> <ul class="view-switcher">
<li classList={{ selected: !params()['by'] || params()['by'] === 'recent' }}> <li classList={{ selected: !args()['by'] || args()['by'] === 'recent' }}>
<button type="button" onClick={() => setBy('')}> <button type="button" onClick={() => (args()['by'] = 'recent')}>
{t('Recent')} {t('Recent')}
</button> </button>
</li> </li>
<li classList={{ selected: params()['by'] === 'rating' }}> <li classList={{ selected: args()['by'] === 'rating' }}>
<button type="button" onClick={() => setBy('rating')}> <button type="button" onClick={() => (args()['by'] = 'rating')}>
{t('Popular')} {t('Popular')}
</button> </button>
</li> </li>
<li classList={{ selected: params()['by'] === 'viewed' }}> <li classList={{ selected: args()['by'] === 'viewed' }}>
<button type="button" onClick={() => setBy('viewed')}> <button type="button" onClick={() => (args()['by'] = 'viewed')}>
{t('Views')} {t('Views')}
</button> </button>
</li> </li>
<li classList={{ selected: params()['by'] === 'commented' }}> <li classList={{ selected: args()['by'] === 'commented' }}>
<button type="button" onClick={() => setBy('commented')}> <button type="button" onClick={() => (args()['by'] = 'commented')}>
{t('Discussing')} {t('Discussing')}
</button> </button>
</li> </li>
@ -99,7 +86,7 @@ export const AuthorPage = (props: AuthorProps) => {
<Show when={articles()?.length > 0}> <Show when={articles()?.length > 0}>
<Beside <Beside
title={t('Topics which supported by author')} title={t('Topics which supported by author')}
values={authorTopics()?.slice(0, 5)} values={getSortedTopics()?.slice(0, 5)}
beside={articles()[0]} beside={articles()[0]}
wrapper={'topic'} wrapper={'topic'}
topicShortDescription={true} topicShortDescription={true}

View File

@ -24,6 +24,8 @@ import {
} from '../../stores/zine/top' } from '../../stores/zine/top'
import { useTopicsStore } from '../../stores/zine/topics' import { useTopicsStore } from '../../stores/zine/topics'
import { loadMorePublished, useArticlesStore } from '../../stores/zine/articles' import { loadMorePublished, useArticlesStore } from '../../stores/zine/articles'
import { sortBy } from '../../utils/sortby'
import { shuffle } from '../../utils'
type HomeProps = { type HomeProps = {
randomTopics: Topic[] randomTopics: Topic[]
@ -39,39 +41,31 @@ const LAYOUTS = ['article', 'prose', 'music', 'video', 'image']
export const HomePage = (props: HomeProps) => { export const HomePage = (props: HomeProps) => {
const [someLayout, setSomeLayout] = createSignal([] as Shout[]) const [someLayout, setSomeLayout] = createSignal([] as Shout[])
const [selectedLayout, setSelectedLayout] = createSignal('article') const [selectedLayout, setSelectedLayout] = createSignal('article')
const [byLayout, setByLayout] = createSignal({} as { [layout: string]: Shout[] }) const [byLayout, setByLayout] = createSignal<{ [layout: string]: Shout[] }>({})
const [byTopic, setByTopic] = createSignal({} as { [topic: string]: Shout[] }) const { getSortedArticles: articles, getArticlesByTopics: byTopic } = useArticlesStore({
const { getSortedArticles } = useArticlesStore({ sortedArticles: props.recentPublishedArticles }) sortedArticles: props.recentPublishedArticles
const articles = createMemo(() => getSortedArticles()) })
const { getRandomTopics, getSortedTopics } = useTopicsStore({ randomTopics: props.randomTopics }) const { getSortedTopics } = useTopicsStore({ topics: sortBy(props.randomTopics, 'shouts') })
createEffect(() => { createEffect(() => {
if (articles() && articles().length > 0 && Object.keys(byTopic()).length === 0) { if (articles() && articles().length > 0 && Object.keys(byTopic()).length === 0) {
console.debug('[home] ' + getRandomTopics().length.toString() + ' random topics loaded')
console.debug('[home] ' + articles().length.toString() + ' overall shouts loaded') console.debug('[home] ' + articles().length.toString() + ' overall shouts loaded')
console.log('[home] preparing published articles...') console.log('[home] preparing published articles...')
// get shouts lists by // get shouts lists by
const bl: { [key: string]: Shout[] } = {} const bl: { [key: string]: Shout[] } = {}
const bt: { [key: string]: Shout[] } = {}
articles().forEach((s: Shout) => { articles().forEach((s: Shout) => {
// by topic
s.topics?.forEach(({ slug }: any) => {
if (!bt[slug || '']) bt[slug || ''] = []
bt[slug as string].push(s)
})
// by layout // by layout
const l = s.layout || 'article' const l = s.layout || 'article'
if (!bl[l]) bl[l] = [] if (!bl[l]) bl[l] = []
bl[l].push(s) bl[l].push(s)
}) })
setByLayout(bl) setByLayout(bl)
setByTopic(bt) console.log('[home] some grouped by layout articles are ready')
console.log('[home] some grouped articles are ready')
} }
}, [articles()]) }, [articles()])
createEffect(() => { createEffect(() => {
if (Object.keys(byLayout()).length > 0 && getSortedTopics()) { if (getSortedTopics() && !selectedLayout()) {
// random special layout pick // random special layout pick
const special = LAYOUTS.filter((la) => la !== 'article') const special = LAYOUTS.filter((la) => la !== 'article')
const layout = special[Math.floor(Math.random() * special.length)] const layout = special[Math.floor(Math.random() * special.length)]
@ -85,7 +79,7 @@ export const HomePage = (props: HomeProps) => {
if (props.topOverallArticles) setTopRated(props.topOverallArticles) if (props.topOverallArticles) setTopRated(props.topOverallArticles)
console.info('[home] mounted') console.info('[home] mounted')
}) })
const getRandomTopics = () => shuffle(getSortedTopics()).slice(0, 12)
return ( return (
<Suspense fallback={t('Loading')}> <Suspense fallback={t('Loading')}>
<Show when={Boolean(articles())}> <Show when={Boolean(articles())}>

View File

@ -1,4 +1,4 @@
import { For, Show, createMemo } from 'solid-js' import { For, Show, createMemo, createEffect, createSignal } from 'solid-js'
import type { Shout, Topic } from '../../graphql/types.gen' import type { Shout, Topic } from '../../graphql/types.gen'
import Row3 from '../Feed/Row3' import Row3 from '../Feed/Row3'
import Row2 from '../Feed/Row2' import Row2 from '../Feed/Row2'
@ -8,9 +8,11 @@ import '../../styles/Topic.scss'
import { FullTopic } from '../Topic/Full' import { FullTopic } from '../Topic/Full'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { params } from '../../stores/router' import { params } from '../../stores/router'
import { useTopicsStore } from '../../stores/zine/topics'
import { useArticlesStore } from '../../stores/zine/articles' import { useArticlesStore } from '../../stores/zine/articles'
import { useStore } from '@nanostores/solid' import { useStore } from '@nanostores/solid'
import { unique } from '../../utils'
import { useTopicsStore } from '../../stores/zine/topics'
import { byCreated, sortBy } from '../../utils/sortby'
interface TopicProps { interface TopicProps {
topic: Topic topic: Topic
@ -19,22 +21,28 @@ interface TopicProps {
export const TopicPage = (props: TopicProps) => { export const TopicPage = (props: TopicProps) => {
const args = useStore(params) const args = useStore(params)
const { getAuthorsByTopic } = useTopicsStore({ topics: [props.topic] }) const { getArticlesByTopics } = useArticlesStore({ sortedArticles: props.topicArticles })
const { getSortedArticles: sortedArticles } = useArticlesStore({ sortedArticles: props.topicArticles })
const topic = createMemo(() => props.topic) const [topicAuthors, setTopicAuthors] = createSignal([])
/* const sortedArticles = createMemo(() => {
const slug = createMemo<string>(() => { const aaa = getArticlesByTopics()[props.topic.slug] || []
let slug = props?.slug aaa.forEach((a: Shout) => {
if (props?.slug.startsWith('@')) slug = slug.replace('@', '') a.topics?.forEach((t: Topic) => {
return slug if (props.topic.slug === t.slug) {
setTopicAuthors((aaa) => [...aaa, a])
}
})
})
return args()['by'] ? sortBy(aaa, args()['by']) : sortBy(aaa, byCreated)
}) })
*/ const { getTopicEntities } = useTopicsStore({ topics: [props.topic] })
const topic = createMemo(() => getTopicEntities()[props.topic.slug] || props.topic)
const title = createMemo(() => { const title = createMemo(() => {
// const m = by() const m = args()['by']
// if (m === 'viewed') return t('Top viewed') if (m === 'viewed') return t('Top viewed')
// if (m === 'rating') return t('Top rated') if (m === 'rating') return t('Top rated')
// if (m === 'commented') return t('Top discussed') if (m === 'commented') return t('Top discussed')
return t('Top recent') return t('Top recent')
}) })
@ -94,7 +102,7 @@ export const TopicPage = (props: TopicProps) => {
<Show when={sortedArticles().length > 5}> <Show when={sortedArticles().length > 5}>
<Beside <Beside
title={t('Topic is supported by')} title={t('Topic is supported by')}
values={getAuthorsByTopic() as any} values={unique(topicAuthors()) as any}
beside={sortedArticles()[6]} beside={sortedArticles()[6]}
wrapper={'author'} wrapper={'author'}
/> />

View File

@ -15,7 +15,7 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate'
<HomePage <HomePage
randomTopics={randomTopics} randomTopics={randomTopics}
recentPublishedArticles={recentPublished} recentPublishedArticles={recentPublished}
topMonthArticles={topMonth} topMonthArticles={[]}
topOverallArticles={topOverall} topOverallArticles={topOverall}
client:load client:load
/> />

View File

@ -97,12 +97,12 @@ export const useArticlesStore = ({ sortedArticles }: InitialState) => {
export const loadMoreAll = () => { export const loadMoreAll = () => {
const searchParams = useStore(params) const searchParams = useStore(params)
const pn = Number.parseInt(searchParams()['page'], 10) const pn = Number.parseInt(searchParams()['page'] || '1', 10) || 1
loadRecentAllArticles({ page: pn + 1 }) loadRecentAllArticles({ page: pn + 1 })
} }
export const loadMorePublished = () => { export const loadMorePublished = () => {
const searchParams = useStore(params) const searchParams = useStore(params)
const pn = Number.parseInt(searchParams()['page'], 10) const pn = Number.parseInt(searchParams()['page'] || '1', 10) || 1
loadRecentPublishedArticles({ page: pn + 1 }) loadRecentPublishedArticles({ page: pn + 1 })
} }

View File

@ -11,7 +11,7 @@ const sortByStore = atom<AuthorsSortBy>('created')
let authorEntitiesStore: WritableAtom<Record<string, Author>> let authorEntitiesStore: WritableAtom<Record<string, Author>>
let sortedAuthorsStore: ReadableAtom<Author[]> let sortedAuthorsStore: ReadableAtom<Author[]>
let authorsByTopicStore: WritableAtom<Record<string, Author[]>>
const initStore = (initial?: Record<string, Author>) => { const initStore = (initial?: Record<string, Author>) => {
if (authorEntitiesStore) { if (authorEntitiesStore) {
return return
@ -61,6 +61,6 @@ export const useAuthorsStore = (initial?: Author[]) => {
const getAuthorEntities = useStore(authorEntitiesStore) const getAuthorEntities = useStore(authorEntitiesStore)
const getSortedAuthors = useStore(sortedAuthorsStore) const getSortedAuthors = useStore(sortedAuthorsStore)
const getAuthorsByTopic = useStore(authorsByTopicStore)
return { getAuthorEntities, getSortedAuthors } return { getAuthorEntities, getSortedAuthors, getAuthorsByTopic }
} }

View File

@ -11,7 +11,6 @@ const sortByStore = atom<TopicsSortBy>('created')
let topicEntitiesStore: WritableAtom<Record<string, Topic>> let topicEntitiesStore: WritableAtom<Record<string, Topic>>
let sortedTopicsStore: ReadableAtom<Topic[]> let sortedTopicsStore: ReadableAtom<Topic[]>
let randomTopicsStore: WritableAtom<Topic[]>
const initStore = (initial?: Record<string, Topic>) => { const initStore = (initial?: Record<string, Topic>) => {
if (topicEntitiesStore) { if (topicEntitiesStore) {
@ -64,18 +63,11 @@ type InitialState = {
randomTopics?: Topic[] randomTopics?: Topic[]
} }
export const useTopicsStore = ({ topics, randomTopics }: InitialState) => { export const useTopicsStore = ({ topics }: InitialState) => {
addTopics(topics) addTopics(topics)
// WIP
if (!randomTopicsStore) {
randomTopicsStore = atom<Topic[]>(randomTopics)
}
const getTopicEntities = useStore(topicEntitiesStore) const getTopicEntities = useStore(topicEntitiesStore)
const getSortedTopics = useStore(sortedTopicsStore) const getSortedTopics = useStore(sortedTopicsStore)
const getRandomTopics = useStore(randomTopicsStore)
// eslint-disable-next-line unicorn/consistent-function-scoping return { getTopicEntities, getSortedTopics }
const getAuthorsByTopic = () => [] // FIXME: useStore(authorsByTopic)
return { getTopicEntities, getSortedTopics, getRandomTopics, getAuthorsByTopic }
} }

View File

@ -21,7 +21,6 @@ import topicsRandomQuery from '../graphql/query/topics-random'
import articlesTopMonth from '../graphql/query/articles-top-month' import articlesTopMonth from '../graphql/query/articles-top-month'
import articlesTopRated from '../graphql/query/articles-top-rated' import articlesTopRated from '../graphql/query/articles-top-rated'
import authorsAll from '../graphql/query/authors-all' import authorsAll from '../graphql/query/authors-all'
import { GRAPHQL_MAX_INT } from 'graphql/type'
import reactionCreate from '../graphql/mutation/reaction-create' import reactionCreate from '../graphql/mutation/reaction-create'
import reactionDestroy from '../graphql/mutation/reaction-destroy' import reactionDestroy from '../graphql/mutation/reaction-destroy'
import reactionUpdate from '../graphql/mutation/reaction-update' import reactionUpdate from '../graphql/mutation/reaction-update'
@ -43,7 +42,7 @@ export const apiClient = {
}, },
getTopMonthArticles: async () => { getTopMonthArticles: async () => {
const response = await publicGraphQLClient.query(articlesTopMonth, { page: 1, size: 10 }).toPromise() const response = await publicGraphQLClient.query(articlesTopMonth, { page: 1, size: 10 }).toPromise()
return response.data.articlesTopMonth return response.data.topMonth
}, },
getRandomTopics: async () => { getRandomTopics: async () => {
const response = await publicGraphQLClient.query(topicsRandomQuery, {}).toPromise() const response = await publicGraphQLClient.query(topicsRandomQuery, {}).toPromise()
@ -69,33 +68,27 @@ export const apiClient = {
return response.data.searchQuery return response.data.searchQuery
}, },
getRecentAllArticles: async ({ getRecentAllArticles: async ({ page, size }: { page?: number; size?: number }): Promise<Shout[]> => {
page = 1,
size = FEED_PAGE_SIZE
}: {
page?: number
size?: number
}): Promise<Shout[]> => {
const response = await publicGraphQLClient const response = await publicGraphQLClient
.query(articlesRecentAll, { .query(articlesRecentAll, {
page, page: page || 1,
size size: size || FEED_PAGE_SIZE
}) })
.toPromise() .toPromise()
return response.data.recentAll return response.data.recentAll
}, },
getRecentPublishedArticles: async ({ getRecentPublishedArticles: async ({
page = 1, page,
size = FEED_PAGE_SIZE size
}: { }: {
page?: number page?: number
size?: number size?: number
}): Promise<Shout[]> => { }): Promise<Shout[]> => {
const response = await publicGraphQLClient const response = await publicGraphQLClient
.query(articlesRecentPublished, { .query(articlesRecentPublished, {
page, page: page || 1,
size size: size || FEED_PAGE_SIZE
}) })
.toPromise() .toPromise()
@ -198,9 +191,7 @@ export const apiClient = {
}, },
getAllAuthors: async () => { getAllAuthors: async () => {
const response = await publicGraphQLClient const response = await publicGraphQLClient.query(authorsAll, { page: 1, size: 9999 }).toPromise()
.query(authorsAll, { page: 1, size: GRAPHQL_MAX_INT })
.toPromise()
return response.data.authorsAll return response.data.authorsAll
}, },
getArticle: async ({ slug }: { slug: string }): Promise<Shout> => { getArticle: async ({ slug }: { slug: string }): Promise<Shout> => {

2174
yarn.lock

File diff suppressed because it is too large Load Diff