refactoring-wpi

This commit is contained in:
tonyrewin 2022-11-13 12:25:31 +03:00
parent e6a14c6fa1
commit 28f52ba745
39 changed files with 283 additions and 284 deletions

View File

@ -2,7 +2,7 @@ import { defineConfig, AstroUserConfig } from 'astro/config'
import vercel from '@astrojs/vercel/serverless' import vercel from '@astrojs/vercel/serverless'
import solidJs from '@astrojs/solid-js' import solidJs from '@astrojs/solid-js'
import type { CSSOptions } from 'vite' import type { CSSOptions } from 'vite'
import { generateScopedNameDefault } from 'postcss-modules/build/scoping' import defaultGenerateScopedName from 'postcss-modules/build/generateScopedName'
import { isDev } from './src/utils/config' import { isDev } from './src/utils/config'
import { visualizer } from 'rollup-plugin-visualizer' import { visualizer } from 'rollup-plugin-visualizer'
@ -16,7 +16,7 @@ const getDevCssClassPrefix = (filename: string): string => {
} }
const devGenerateScopedName = (name: string, filename: string, css: string) => const devGenerateScopedName = (name: string, filename: string, css: string) =>
getDevCssClassPrefix(filename) + '_' + generateScopedNameDefault(name, filename, css) getDevCssClassPrefix(filename) + '_' + defaultGenerateScopedName(name, filename, css)
const css: CSSOptions = { const css: CSSOptions = {
preprocessorOptions: { preprocessorOptions: {
@ -25,7 +25,7 @@ const css: CSSOptions = {
} }
}, },
modules: { modules: {
generateScopedName: isDev ? devGenerateScopedName : generateScopedNameDefault, generateScopedName: isDev ? devGenerateScopedName : defaultGenerateScopedName,
localsConvention: null localsConvention: null
} }
} }
@ -67,7 +67,7 @@ const astroConfig: AstroUserConfig = {
} }
*/ */
}, },
external: ['@aws-sdk/clients/s3'] external: []
} }
}, },
css css

View File

@ -94,7 +94,7 @@
"nanostores": "^0.7.0", "nanostores": "^0.7.0",
"orderedmap": "^2.1.0", "orderedmap": "^2.1.0",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"postcss-modules": "^6.0.0", "postcss-modules": "5.0.0",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"prettier-eslint": "^15.0.1", "prettier-eslint": "^15.0.1",
"prosemirror-commands": "^1.3.1", "prosemirror-commands": "^1.3.1",

View File

@ -63,7 +63,7 @@ specifiers:
nanostores: ^0.7.0 nanostores: ^0.7.0
orderedmap: ^2.1.0 orderedmap: ^2.1.0
postcss: ^8.4.19 postcss: ^8.4.19
postcss-modules: ^6.0.0 postcss-modules: 5.0.0
prettier: ^2.7.1 prettier: ^2.7.1
prettier-eslint: ^15.0.1 prettier-eslint: ^15.0.1
prosemirror-commands: ^1.3.1 prosemirror-commands: ^1.3.1
@ -173,7 +173,7 @@ devDependencies:
nanostores: 0.7.0 nanostores: 0.7.0
orderedmap: 2.1.0 orderedmap: 2.1.0
postcss: 8.4.19 postcss: 8.4.19
postcss-modules: 6.0.0_postcss@8.4.19 postcss-modules: 5.0.0_postcss@8.4.19
prettier: 2.7.1 prettier: 2.7.1
prettier-eslint: 15.0.1 prettier-eslint: 15.0.1
prosemirror-commands: 1.3.1 prosemirror-commands: 1.3.1
@ -6087,6 +6087,10 @@ packages:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
dev: true dev: true
/icss-replace-symbols/1.1.0:
resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==}
dev: true
/icss-utils/5.1.0_postcss@8.4.19: /icss-utils/5.1.0_postcss@8.4.19:
resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
engines: {node: ^10 || ^12 || >= 14} engines: {node: ^10 || ^12 || >= 14}
@ -8577,13 +8581,13 @@ packages:
postcss: 8.4.19 postcss: 8.4.19
dev: true dev: true
/postcss-modules/6.0.0_postcss@8.4.19: /postcss-modules/5.0.0_postcss@8.4.19:
resolution: {integrity: sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==} resolution: {integrity: sha512-rGvpTDOM3//3Ysn3Xtvhzaj8ab984wKCpP02TEF559tLbUjNay3RQDpPxb7BREmfBtJm3/1WbQOZ7fSXwYLZ/w==}
peerDependencies: peerDependencies:
postcss: ^8.0.0 postcss: ^8.0.0
dependencies: dependencies:
generic-names: 4.0.0 generic-names: 4.0.0
icss-utils: 5.1.0_postcss@8.4.19 icss-replace-symbols: 1.1.0
lodash.camelcase: 4.3.0 lodash.camelcase: 4.3.0
postcss: 8.4.19 postcss: 8.4.19
postcss-modules-extract-imports: 3.0.0_postcss@8.4.19 postcss-modules-extract-imports: 3.0.0_postcss@8.4.19

4
public/icons/audio.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M11.1853 7.68061L11.1746 2.64865L5.05266 3.68025C5.04861 8.28981 5.04659 10.9099 5.04659 11.5405L5.04077 11.5326C4.95203 12.9109 3.85883 14 2.52329 14C1.12972 14 0 12.8142 0 11.3514C0 9.88854 1.12972 8.7027 2.52329 8.7027C2.77381 8.7027 3.0158 8.74102 3.24423 8.81239V1.7027L12.9769 0C13.0077 6.85087 13.0077 10.3193 12.9769 10.4054L12.9711 10.398C12.8821 11.776 11.789 12.8649 10.4536 12.8649C9.06007 12.8649 7.93035 11.679 7.93035 10.2162C7.93035 8.75341 9.06007 7.56757 10.4536 7.56757C10.7081 7.56757 10.9537 7.60709 11.1853 7.68061Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 675 B

View File

@ -0,0 +1 @@
<svg width="12" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.969 1.688l6.181 2.18v12.88H12V2.722L4.2 0 0 1.47v13.668L7.436 18H8.55V4.532L2.147 2.265l1.822-.577z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 216 B

View File

@ -1,10 +1,10 @@
import { PageWrap } from '../Wraps/PageWrap' import { PageWrap } from '../Wraps/PageWrap'
import { LayoutView } from '../Views/LayoutView' import { LayoutType, LayoutView } from '../Views/LayoutView'
import type { PageProps } from '../types' import type { PageProps } from '../types'
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js' import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
import { resetSortedArticles } from '../../stores/zine/articles' import { resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
import { loadLayoutShouts } from '../../stores/zine/layouts' import { loadRecentLayoutShouts } from '../../stores/zine/layouts'
import { Loading } from '../Loading' import { Loading } from '../Loading'
const PER_PAGE = 50 const PER_PAGE = 50
@ -12,7 +12,7 @@ const PER_PAGE = 50
export const LayoutShoutsPage = (props: PageProps) => { export const LayoutShoutsPage = (props: PageProps) => {
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.shouts)) const [isLoaded, setIsLoaded] = createSignal(Boolean(props.shouts))
const layout = createMemo(() => { const layout = createMemo<LayoutType>(() => {
const { page: getPage } = useRouter() const { page: getPage } = useRouter()
const page = getPage() const page = getPage()
@ -21,7 +21,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
throw new Error('ts guard') throw new Error('ts guard')
} }
return page.params.layout return page.params.layout as LayoutType
}) })
onMount(async () => { onMount(async () => {
@ -29,7 +29,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
return return
} }
await loadLayoutShouts({ layout: layout(), amount: PER_PAGE, offset: 0 }) await loadRecentLayoutShouts({ layout: layout(), amount: PER_PAGE, offset: 0 })
setIsLoaded(true) setIsLoaded(true)
}) })
@ -39,7 +39,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
return ( return (
<PageWrap> <PageWrap>
<Show when={isLoaded()} fallback={<Loading />}> <Show when={isLoaded()} fallback={<Loading />}>
<LayoutView layout={layout()} shouts={props.shouts} /> <LayoutView layout={layout() as LayoutType} shouts={props.shouts} />
</Show> </Show>
</PageWrap> </PageWrap>
) )

View File

@ -6,7 +6,7 @@ import { handleClientRouteLinkClick } from '../../stores/router'
// by: '' | 'topics' | 'authors' | 'reacted' // by: '' | 'topics' | 'authors' | 'reacted'
// } // }
export const FeedSettingsView = () => { export const FeedSettingsView = (_props) => {
return ( return (
<div class="container"> <div class="container">
<h1>{t('Feed settings')}</h1> <h1>{t('Feed settings')}</h1>

View File

@ -5,21 +5,18 @@ import { Row2 } from '../Feed/Row2'
import { Beside } from '../Feed/Beside' import { Beside } from '../Feed/Beside'
import styles from '../../styles/Topic.module.scss' import styles from '../../styles/Topic.module.scss'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { useRouter } from '../../stores/router' import { useLayoutsStore } from '../../stores/zine/layouts'
import { useArticlesStore } from '../../stores/zine/articles'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll' import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import { splitToPages } from '../../utils/splitToPages' import { splitToPages } from '../../utils/splitToPages'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import Slider from '../Feed/Slider' import Slider from '../Feed/Slider'
import { Row1 } from '../Feed/Row1' import { Row1 } from '../Feed/Row1'
import { loadLayoutShouts } from '../../stores/zine/layouts' import { loadRecentLayoutShouts } from '../../stores/zine/layouts'
type LayoutPageSearchParams = { export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature'
layout: 'audio' | 'video' | 'image' | 'literature'
}
interface LayoutProps { interface LayoutProps {
layout: string layout: LayoutType
shouts: Shout[] shouts: Shout[]
} }
@ -27,34 +24,30 @@ export const PRERENDERED_ARTICLES_COUNT = 21
const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3 const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
export const LayoutView = (props: LayoutProps) => { export const LayoutView = (props: LayoutProps) => {
const { searchParams, changeSearchParam } = useRouter<LayoutPageSearchParams>() const layout = createMemo<LayoutType>(() => props.layout)
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const { sortedLayoutShouts } = useLayoutsStore(layout(), props.shouts)
const { sortedArticles } = useArticlesStore({ sortedArticles: props.shouts }) const sortedArticles = createMemo(() => sortedLayoutShouts().get(layout()))
const layout = createMemo(() => props.layout) const loadMoreLayout = async (kind: LayoutType) => {
const loadMoreLayout = async (kind: string) => {
saveScrollPosition() saveScrollPosition()
const { hasMore } = await loadLayoutShouts({ const { hasMore } = await loadRecentLayoutShouts({
layout: kind, layout: kind,
amount: LOAD_MORE_PAGE_SIZE, amount: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length offset: sortedArticles().length
}) })
setIsLoadMoreButtonVisible(hasMore) setIsLoadMoreButtonVisible(hasMore)
restoreScrollPosition() restoreScrollPosition()
} }
onMount(async () => { onMount(async () => {
if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) { if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) {
loadMoreLayout(searchParams().layout) loadMoreLayout(layout())
} }
}) })
const title = createMemo(() => { const title = createMemo(() => {
const l = searchParams().layout const l = layout()
if (l === 'audio') return t('Audio') if (l === 'audio') return t('Audio')
if (l === 'video') return t('Video') if (l === 'video') return t('Video')
if (l === 'image') return t('Artworks') if (l === 'image') return t('Artworks')
@ -70,16 +63,16 @@ export const LayoutView = (props: LayoutProps) => {
<div class={clsx(styles.groupControls, 'row group__controls')}> <div class={clsx(styles.groupControls, 'row group__controls')}>
<div class="col-md-8"> <div class="col-md-8">
<ul class="view-switcher"> <ul class="view-switcher">
<li classList={{ selected: searchParams().layout === 'audio' }}> <li classList={{ selected: layout() === 'audio' }}>
<a href="/expo/audio">{t('Audio')}</a> <a href="/expo/audio">{t('Audio')}</a>
</li> </li>
<li classList={{ selected: searchParams().layout === 'video' }}> <li classList={{ selected: layout() === 'video' }}>
<a href="/expo/video">{t('Video')}</a> <a href="/expo/video">{t('Video')}</a>
</li> </li>
<li classList={{ selected: searchParams().layout === 'image' }}> <li classList={{ selected: layout() === 'image' }}>
<a href="/expo/image">{t('Artworks')}</a> <a href="/expo/image">{t('Artworks')}</a>
</li> </li>
<li classList={{ selected: searchParams().layout === 'literature' || !searchParams().layout }}> <li classList={{ selected: layout() === 'literature' }}>
<a href="/expo/literature">{t('Literature')}</a> <a href="/expo/literature">{t('Literature')}</a>
</li> </li>
</ul> </ul>
@ -129,7 +122,7 @@ export const LayoutView = (props: LayoutProps) => {
<Show when={isLoadMoreButtonVisible()}> <Show when={isLoadMoreButtonVisible()}>
<p class="load-more-container"> <p class="load-more-container">
<button class="button" onClick={() => loadMoreLayout(searchParams().layout)}> <button class="button" onClick={() => loadMoreLayout(layout())}>
{t('Load more')} {t('Load more')}
</button> </button>
</p> </p>

View File

@ -0,0 +1,40 @@
import { gql } from '@urql/core'
export default gql`
query RecentShoutsForLayout($layout: String!, $amount: Int, $offset: Int) {
recentLayoutShouts(layout: $layout, amount: $amount, offset: $offset) {
_id: slug
title
subtitle
layout
slug
cover
# community
mainTopic
topics {
title
body
slug
stat {
_id: shouts
shouts
authors
followers
}
}
authors {
_id: slug
name
slug
userpic
}
createdAt
publishedAt
stat {
_id: viewed
viewed
reacted
}
}
}
`

View File

@ -0,0 +1,40 @@
import { gql } from '@urql/core'
export default gql`
query TopMonthShoutsForLayout($layout: String!, $amount: Int, $offset: Int) {
topMonthLayoutShouts(layout: $layout, amount: $amount, offset: $offset) {
_id: slug
title
subtitle
layout
slug
cover
# community
mainTopic
topics {
title
body
slug
stat {
_id: shouts
shouts
authors
followers
}
}
authors {
_id: slug
name
slug
userpic
}
createdAt
publishedAt
stat {
_id: viewed
viewed
reacted
}
}
}
`

View File

@ -1,8 +1,8 @@
import { gql } from '@urql/core' import { gql } from '@urql/core'
export default gql` export default gql`
query ShoutsForLayoutQuery($amount: Int, $offset: Int, $layout: String) { query TopShoutsForLayout($layout: String!, $amount: Int, $offset: Int) {
shoutsByLayout(amount: $amount, offset: $offset, layout: $layout) { topLayoutShouts(layout: $layout, amount: $amount, offset: $offset) {
_id: slug _id: slug
title title
subtitle subtitle

View File

@ -1,5 +1,5 @@
--- ---
import { setLocale } from './stores/ui'; import { setLocale } from './stores/ui'
import './styles/app.scss' import './styles/app.scss'
import { t } from './utils/intl' import { t } from './utils/intl'

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { Root } from '../components/Root' import { Root } from '../components/Root'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../components/Root' import { Root } from '../components/Root'
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { apiClient } from '../utils/apiClient' import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -20,6 +20,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root article={article} client:load /> <Root article={article} client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -13,6 +13,6 @@ Astro.response.headers.set(
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../../../components/Root' import { Root } from '../../../components/Root'
import Zine from '../../../main.astro' import Prerendered from '../../../main.astro'
import { apiClient } from '../../../utils/apiClient' import { apiClient } from '../../../utils/apiClient'
import { initRouter } from '../../../stores/router' import { initRouter } from '../../../stores/router'
import { PRERENDERED_ARTICLES_COUNT } from '../../../components/Views/Author' import { PRERENDERED_ARTICLES_COUNT } from '../../../components/Views/Author'
@ -15,6 +15,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root shouts={shouts} author={author} client:load /> <Root shouts={shouts} author={author} client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../components/Root' import { Root } from '../components/Root'
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { apiClient } from '../utils/apiClient' import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -10,6 +10,6 @@ const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)
--- ---
<Zine> <Prerendered>
<Root allAuthors={authors} client:load /> <Root allAuthors={authors} client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { Root } from '../components/Root' import { Root } from '../components/Root'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../components/Root' import { Root } from '../components/Root'
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
const { pathname, search } = Astro.url const { pathname, search } = Astro.url
@ -8,6 +8,6 @@ initRouter(pathname, search)
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { apiClient } from '../../utils/apiClient' import { apiClient } from '../../utils/apiClient'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
@ -17,7 +17,7 @@ const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)
--- ---
<Zine> <Prerendered>
<Root shouts={shouts} layout={layout} client:load /> <Root shouts={shouts} layout={layout} client:load />
</Zine> </Prerendered>

View File

@ -1,12 +1,12 @@
--- ---
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'
const { pathname, search } = Astro.url const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)
--- ---
<Zine> <Prerendered>
<Root client:load /> <Root client:load />
</Zine> </Prerendered>

View File

@ -0,0 +1,12 @@
---
import Prerendered from '../../main.astro'
import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router'
const { pathname, search } = Astro.url
initRouter(pathname, search)
---
<Prerendered>
<Root client:load />
</Prerendered>

View File

@ -1,13 +0,0 @@
---
import Zine from '../../main.astro'
import { FeedSettings } from '../../components/Views/FeedSettings'
import { initRouter } from '../../stores/router'
const { pathname, search } = Astro.url
initRouter(pathname, search)
---
<Zine>
<FeedSettings client:load />
</Zine>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { Root } from '../components/Root' import { Root } from '../components/Root'
import { apiClient } from '../utils/apiClient' import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -10,6 +10,6 @@ const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)
--- ---
<Zine> <Prerendered>
<Root chats={chatrooms} client:load /> <Root chats={chatrooms} client:load />
</Zine> </Prerendered>

View File

@ -1,5 +1,5 @@
--- ---
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { Root } from '../components/Root' import { Root } from '../components/Root'
import { apiClient } from '../utils/apiClient' import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -14,7 +14,7 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root randomTopics={randomTopics} shouts={articles} client:load /> <Root randomTopics={randomTopics} shouts={articles} client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../components/Root' import { Root } from '../components/Root'
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { apiClient } from '../utils/apiClient' import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -12,6 +12,6 @@ const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)
--- ---
<Zine> <Prerendered>
<Root searchResults={searchResults} client:load /> <Root searchResults={searchResults} client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../../components/Root' import { Root } from '../../components/Root'
import Zine from '../../main.astro' import Prerendered from '../../main.astro'
import { apiClient } from '../../utils/apiClient' import { apiClient } from '../../utils/apiClient'
import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Topic' import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Topic'
@ -16,6 +16,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate') Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
--- ---
<Zine> <Prerendered>
<Root shouts={shouts} topic={topic} client:load /> <Root shouts={shouts} topic={topic} client:load />
</Zine> </Prerendered>

View File

@ -1,6 +1,6 @@
--- ---
import { Root } from '../components/Root' import { Root } from '../components/Root'
import Zine from '../main.astro' import Prerendered from '../main.astro'
import { apiClient } from '../utils/apiClient' import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
@ -10,7 +10,7 @@ const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)
--- ---
<Zine> <Prerendered>
<Root allTopics={topics} client:load /> <Root allTopics={topics} client:load />
</Zine> </Prerendered>

View File

@ -1,196 +1,99 @@
import type { Author, Shout, ShoutInput, Topic } from '../../graphql/types.gen' import type { Shout } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient' import { apiClient } from '../../utils/apiClient'
import { addAuthorsByTopic } from './authors' import { useArticlesStore } from './articles'
import { addTopicsByAuthor } from './topics'
import { byStat } from '../../utils/sortby'
import { createSignal } from 'solid-js' import { createSignal } from 'solid-js'
import { createLazyMemo } from '@solid-primitives/memo' import type { LayoutType } from '../../components/Views/LayoutView'
import { byCreated } from '../../utils/sortby'
const [sortedArticles, setSortedArticles] = createSignal<Shout[]>([]) const [sortedLayoutShouts, setSortedLayoutShouts] = createSignal<Map<LayoutType, Shout[]>>(new Map())
const [articleEntities, setArticleEntities] = createSignal<{ [articleSlug: string]: Shout }>({})
const [topArticles, setTopArticles] = createSignal<Shout[]>([]) const addLayoutShouts = (layout: LayoutType, shouts: Shout[]) => {
const [topMonthArticles, setTopMonthArticles] = createSignal<Shout[]>([]) setSortedLayoutShouts((prevSorted: Map<LayoutType, Shout[]>) => {
const siblings = prevSorted.get(layout)
const articlesByLayout = createLazyMemo(() => { if (Boolean(siblings)) {
return Object.values(articleEntities()).reduce((acc, article) => { const uniqued = Array.from(new Set([...siblings, ...shouts]))
if (!acc[article.layout]) { prevSorted.set(layout, uniqued)
acc[article.layout] = []
}
acc[article.layout].push(article)
return acc
}, {} as { [layout: string]: Shout[] })
})
const topViewedArticles = createLazyMemo(() => {
const result = Object.values(articleEntities())
result.sort(byStat('viewed'))
return result
})
const topCommentedArticles = createLazyMemo(() => {
const result = Object.values(articleEntities())
result.sort(byStat('commented'))
return result
})
// eslint-disable-next-line sonarjs/cognitive-complexity
const addArticles = (...args: Shout[][]) => {
const allArticles = args.flatMap((articles) => articles || [])
const newArticleEntities = allArticles.reduce((acc, article) => {
acc[article.slug] = article
return acc
}, {} as { [articleSLug: string]: Shout })
setArticleEntities((prevArticleEntities) => {
return {
...prevArticleEntities,
...newArticleEntities
} }
return prevSorted
}) })
const authorsByTopic = allArticles.reduce((acc, article) => {
const { authors, topics } = article
topics.forEach((topic) => {
if (!acc[topic.slug]) {
acc[topic.slug] = []
}
authors.forEach((author) => {
if (!acc[topic.slug].some((a) => a.slug === author.slug)) {
acc[topic.slug].push(author)
}
})
})
return acc
}, {} as { [topicSlug: string]: Author[] })
addAuthorsByTopic(authorsByTopic)
const topicsByAuthor = allArticles.reduce((acc, article) => {
const { authors, topics } = article
authors.forEach((author) => {
if (!acc[author.slug]) {
acc[author.slug] = []
}
topics.forEach((topic) => {
if (!acc[author.slug].some((t) => t.slug === topic.slug)) {
acc[author.slug].push(topic)
}
})
})
return acc
}, {} as { [authorSlug: string]: Topic[] })
addTopicsByAuthor(topicsByAuthor)
} }
const addSortedArticles = (articles: Shout[]) => { export const resetSortedLayoutShouts = () => {
setSortedArticles((prevSortedArticles) => [...prevSortedArticles, ...articles]) setSortedLayoutShouts(new Map())
} }
export const loadLayoutShouts = async ({ export const loadRecentLayoutShouts = async ({
layout, layout,
amount, amount,
offset offset
}: { }: {
layout: string layout: LayoutType
amount: number amount: number
offset?: number offset?: number
}): Promise<{ hasMore: boolean }> => { }): Promise<{ hasMore: boolean }> => {
const layoutShouts: Shout[] = await apiClient.getLayoutShouts({ layout, amount, offset }) const layoutShouts: Shout[] = await apiClient.getRecentLayoutShouts({ layout, amount, offset })
const hasMore = layoutShouts.length > amount const hasMore = layoutShouts.length < amount
if (hasMore) layoutShouts.splice(-1)
if (hasMore) { const sortedArticles = layoutShouts.sort(byCreated)
layoutShouts.splice(-1) const { articlesByLayout } = useArticlesStore({ sortedArticles })
} addLayoutShouts(layout, articlesByLayout()[layout])
addArticles(layoutShouts)
addSortedArticles(layoutShouts)
return { hasMore } return { hasMore }
} }
export const resetSortedArticles = () => { export const loadTopMonthLayoutShouts = async (
setSortedArticles([]) layout: LayoutType,
amount: number,
offset: number
): Promise<{ hasMore: boolean }> => {
const shouts = await apiClient.getTopMonthLayoutShouts({ layout })
const hasMore = shouts.length < amount
if (hasMore) shouts.splice(-1)
addLayoutShouts(layout, shouts)
return { hasMore }
} }
export const loadTopMonthArticles = async (): Promise<void> => { export const loadTopLayoutShouts = async (
const articles = await apiClient.getTopMonthArticles() layout: LayoutType,
addArticles(articles) amount,
setTopMonthArticles(articles) offset
} ): Promise<{ hasMore: boolean }> => {
const shouts = await apiClient.getTopLayoutShouts({ layout })
export const loadTopArticles = async (): Promise<void> => { const hasMore = shouts.length < amount
const articles = await apiClient.getTopArticles() if (hasMore) shouts.splice(-1)
addArticles(articles) addLayoutShouts(layout, shouts)
setTopArticles(articles) return { hasMore }
} }
export const loadSearchResults = async ({ export const loadSearchResults = async ({
layout,
query, query,
limit, limit,
offset offset
}: { }: {
layout: LayoutType
query: string query: string
limit?: number limit?: number
offset?: number offset?: number
}): Promise<void> => { }): Promise<void> => {
const newArticles = await apiClient.getSearchResults({ query, limit, offset }) const newLayoutShouts = await apiClient.getSearchResults({ layout, query, limit, offset })
addArticles(newArticles) addLayoutShouts(layout, newLayoutShouts)
addSortedArticles(newArticles)
}
export const incrementView = async ({ articleSlug }: { articleSlug: string }): Promise<void> => {
await apiClient.incrementView({ articleSlug })
}
export const loadArticle = async ({ slug }: { slug: string }): Promise<void> => {
const article = await apiClient.getArticle({ slug })
if (!article) {
throw new Error(`Can't load article, slug: "${slug}"`)
}
addArticles([article])
}
export const createArticle = async ({ article }: { article: ShoutInput }) => {
try {
await apiClient.createArticle({ article })
} catch (error) {
console.error(error)
}
} }
type InitialState = { type InitialState = {
sortedArticles?: Shout[] sortedLayoutShouts?: Shout[]
topRatedArticles?: Shout[] topRatedLayoutShouts?: Shout[]
topRatedMonthArticles?: Shout[] topRatedMonthLayoutShouts?: Shout[]
} }
export const useArticlesStore = (initialState: InitialState = {}) => { export const useLayoutsStore = (layout: LayoutType, initialData: Shout[]) => {
addArticles([...(initialState.sortedArticles || [])]) addLayoutShouts(layout, initialData || [])
if (initialState.sortedArticles) {
setSortedArticles([...initialState.sortedArticles])
}
return { return {
articleEntities, addLayoutShouts,
sortedArticles, sortedLayoutShouts,
topArticles, loadSearchResults,
topMonthArticles, loadRecentLayoutShouts,
topViewedArticles, loadTopMonthLayoutShouts,
topCommentedArticles, loadTopLayoutShouts
articlesByLayout
} }
} }

View File

@ -29,7 +29,10 @@ import authorsBySlugs from '../graphql/query/authors-by-slugs'
import incrementView from '../graphql/mutation/increment-view' import incrementView from '../graphql/mutation/increment-view'
import createArticle from '../graphql/mutation/article-create' import createArticle from '../graphql/mutation/article-create'
import myChats from '../graphql/query/my-chats' import myChats from '../graphql/query/my-chats'
import getLayout from '../graphql/query/articles-for-layout' import getRecentByLayout from '../graphql/query/layout-recent'
import getTopByLayout from '../graphql/query/layout-top'
import getTopMonthByLayout from '../graphql/query/layout-top-month'
import type { LayoutType } from '../components/Views/LayoutView'
const FEED_SIZE = 50 const FEED_SIZE = 50
@ -147,11 +150,13 @@ export const apiClient = {
getSearchResults: async ({ getSearchResults: async ({
query, query,
limit = FEED_SIZE, limit = FEED_SIZE,
offset = 0 offset = 0,
layout = 'literature'
}: { }: {
query: string query: string
limit: number limit: number
offset?: number offset?: number
layout?: LayoutType
}): Promise<Shout[]> => { }): Promise<Shout[]> => {
const response = await publicGraphQLClient const response = await publicGraphQLClient
.query(searchResults, { .query(searchResults, {
@ -336,8 +341,18 @@ export const apiClient = {
const resp = await privateGraphQLClient.query(myChats, payload).toPromise() const resp = await privateGraphQLClient.query(myChats, payload).toPromise()
return resp.data.myChats return resp.data.myChats
}, },
getLayoutShouts: async ({ layout = 'article', amount = 50, offset = 0 }) => { getRecentLayoutShouts: async ({ layout = 'article', amount = 50, offset = 0 }) => {
const resp = await publicGraphQLClient.query(getLayout, { amount, offset, layout }).toPromise() const resp = await publicGraphQLClient.query(getRecentByLayout, { amount, offset, layout }).toPromise()
return resp.data.shoutsByLayout return resp.data.recentLayoutShouts
},
getTopLayoutShouts: async ({ layout = 'article', amount = 50, offset = 0 }) => {
const resp = await publicGraphQLClient.query(getTopByLayout, { amount, offset, layout }).toPromise()
return resp.data.topLayoutShouts
},
getTopMonthLayoutShouts: async ({ layout = 'article', amount = 50, offset = 0 }) => {
const resp = await publicGraphQLClient
.query(getTopMonthByLayout, { amount, offset, layout })
.toPromise()
return resp.data.topMonthLayoutShouts
} }
} }