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 solidJs from '@astrojs/solid-js'
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 { visualizer } from 'rollup-plugin-visualizer'
@ -16,7 +16,7 @@ const getDevCssClassPrefix = (filename: string): string => {
}
const devGenerateScopedName = (name: string, filename: string, css: string) =>
getDevCssClassPrefix(filename) + '_' + generateScopedNameDefault(name, filename, css)
getDevCssClassPrefix(filename) + '_' + defaultGenerateScopedName(name, filename, css)
const css: CSSOptions = {
preprocessorOptions: {
@ -25,7 +25,7 @@ const css: CSSOptions = {
}
},
modules: {
generateScopedName: isDev ? devGenerateScopedName : generateScopedNameDefault,
generateScopedName: isDev ? devGenerateScopedName : defaultGenerateScopedName,
localsConvention: null
}
}
@ -67,7 +67,7 @@ const astroConfig: AstroUserConfig = {
}
*/
},
external: ['@aws-sdk/clients/s3']
external: []
}
},
css

View File

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

View File

@ -63,7 +63,7 @@ specifiers:
nanostores: ^0.7.0
orderedmap: ^2.1.0
postcss: ^8.4.19
postcss-modules: ^6.0.0
postcss-modules: 5.0.0
prettier: ^2.7.1
prettier-eslint: ^15.0.1
prosemirror-commands: ^1.3.1
@ -173,7 +173,7 @@ devDependencies:
nanostores: 0.7.0
orderedmap: 2.1.0
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-eslint: 15.0.1
prosemirror-commands: 1.3.1
@ -6087,6 +6087,10 @@ packages:
safer-buffer: 2.1.2
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:
resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
engines: {node: ^10 || ^12 || >= 14}
@ -8577,13 +8581,13 @@ packages:
postcss: 8.4.19
dev: true
/postcss-modules/6.0.0_postcss@8.4.19:
resolution: {integrity: sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==}
/postcss-modules/5.0.0_postcss@8.4.19:
resolution: {integrity: sha512-rGvpTDOM3//3Ysn3Xtvhzaj8ab984wKCpP02TEF559tLbUjNay3RQDpPxb7BREmfBtJm3/1WbQOZ7fSXwYLZ/w==}
peerDependencies:
postcss: ^8.0.0
dependencies:
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
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 { LayoutView } from '../Views/LayoutView'
import { LayoutType, LayoutView } from '../Views/LayoutView'
import type { PageProps } from '../types'
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
import { resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router'
import { loadLayoutShouts } from '../../stores/zine/layouts'
import { loadRecentLayoutShouts } from '../../stores/zine/layouts'
import { Loading } from '../Loading'
const PER_PAGE = 50
@ -12,7 +12,7 @@ const PER_PAGE = 50
export const LayoutShoutsPage = (props: PageProps) => {
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.shouts))
const layout = createMemo(() => {
const layout = createMemo<LayoutType>(() => {
const { page: getPage } = useRouter()
const page = getPage()
@ -21,7 +21,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
throw new Error('ts guard')
}
return page.params.layout
return page.params.layout as LayoutType
})
onMount(async () => {
@ -29,7 +29,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
return
}
await loadLayoutShouts({ layout: layout(), amount: PER_PAGE, offset: 0 })
await loadRecentLayoutShouts({ layout: layout(), amount: PER_PAGE, offset: 0 })
setIsLoaded(true)
})
@ -39,7 +39,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
return (
<PageWrap>
<Show when={isLoaded()} fallback={<Loading />}>
<LayoutView layout={layout()} shouts={props.shouts} />
<LayoutView layout={layout() as LayoutType} shouts={props.shouts} />
</Show>
</PageWrap>
)

View File

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

View File

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

View File

@ -1,5 +1,5 @@
---
import { setLocale } from './stores/ui';
import { setLocale } from './stores/ui'
import './styles/app.scss'
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 { initRouter } from '../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---
<Zine>
<Prerendered>
<Root client:load />
</Zine>
</Prerendered>

View File

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

View File

@ -1,6 +1,6 @@
---
import { Root } from '../../../components/Root'
import Zine from '../../../main.astro'
import Prerendered from '../../../main.astro'
import { apiClient } from '../../../utils/apiClient'
import { initRouter } from '../../../stores/router'
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')
---
<Zine>
<Prerendered>
<Root shouts={shouts} author={author} client:load />
</Zine>
</Prerendered>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
---
import { Root } from '../../components/Root'
import Zine from '../../main.astro'
import Prerendered from '../../main.astro'
import { apiClient } from '../../utils/apiClient'
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')
---
<Zine>
<Prerendered>
<Root shouts={shouts} topic={topic} client:load />
</Zine>
</Prerendered>

View File

@ -1,6 +1,6 @@
---
import { Root } from '../components/Root'
import Zine from '../main.astro'
import Prerendered from '../main.astro'
import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router'
@ -10,7 +10,7 @@ const { pathname, search } = Astro.url
initRouter(pathname, search)
---
<Zine>
<Prerendered>
<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 { addAuthorsByTopic } from './authors'
import { addTopicsByAuthor } from './topics'
import { byStat } from '../../utils/sortby'
import { useArticlesStore } from './articles'
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 [articleEntities, setArticleEntities] = createSignal<{ [articleSlug: string]: Shout }>({})
const [sortedLayoutShouts, setSortedLayoutShouts] = createSignal<Map<LayoutType, Shout[]>>(new Map())
const [topArticles, setTopArticles] = createSignal<Shout[]>([])
const [topMonthArticles, setTopMonthArticles] = createSignal<Shout[]>([])
const articlesByLayout = createLazyMemo(() => {
return Object.values(articleEntities()).reduce((acc, article) => {
if (!acc[article.layout]) {
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
const addLayoutShouts = (layout: LayoutType, shouts: Shout[]) => {
setSortedLayoutShouts((prevSorted: Map<LayoutType, Shout[]>) => {
const siblings = prevSorted.get(layout)
if (Boolean(siblings)) {
const uniqued = Array.from(new Set([...siblings, ...shouts]))
prevSorted.set(layout, uniqued)
}
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[]) => {
setSortedArticles((prevSortedArticles) => [...prevSortedArticles, ...articles])
export const resetSortedLayoutShouts = () => {
setSortedLayoutShouts(new Map())
}
export const loadLayoutShouts = async ({
export const loadRecentLayoutShouts = async ({
layout,
amount,
offset
}: {
layout: string
layout: LayoutType
amount: number
offset?: number
}): Promise<{ hasMore: boolean }> => {
const layoutShouts: Shout[] = await apiClient.getLayoutShouts({ layout, amount, offset })
const hasMore = layoutShouts.length > amount
if (hasMore) {
layoutShouts.splice(-1)
}
addArticles(layoutShouts)
addSortedArticles(layoutShouts)
const layoutShouts: Shout[] = await apiClient.getRecentLayoutShouts({ layout, amount, offset })
const hasMore = layoutShouts.length < amount
if (hasMore) layoutShouts.splice(-1)
const sortedArticles = layoutShouts.sort(byCreated)
const { articlesByLayout } = useArticlesStore({ sortedArticles })
addLayoutShouts(layout, articlesByLayout()[layout])
return { hasMore }
}
export const resetSortedArticles = () => {
setSortedArticles([])
export const loadTopMonthLayoutShouts = async (
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> => {
const articles = await apiClient.getTopMonthArticles()
addArticles(articles)
setTopMonthArticles(articles)
}
export const loadTopArticles = async (): Promise<void> => {
const articles = await apiClient.getTopArticles()
addArticles(articles)
setTopArticles(articles)
export const loadTopLayoutShouts = async (
layout: LayoutType,
amount,
offset
): Promise<{ hasMore: boolean }> => {
const shouts = await apiClient.getTopLayoutShouts({ layout })
const hasMore = shouts.length < amount
if (hasMore) shouts.splice(-1)
addLayoutShouts(layout, shouts)
return { hasMore }
}
export const loadSearchResults = async ({
layout,
query,
limit,
offset
}: {
layout: LayoutType
query: string
limit?: number
offset?: number
}): Promise<void> => {
const newArticles = await apiClient.getSearchResults({ query, limit, offset })
addArticles(newArticles)
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)
}
const newLayoutShouts = await apiClient.getSearchResults({ layout, query, limit, offset })
addLayoutShouts(layout, newLayoutShouts)
}
type InitialState = {
sortedArticles?: Shout[]
topRatedArticles?: Shout[]
topRatedMonthArticles?: Shout[]
sortedLayoutShouts?: Shout[]
topRatedLayoutShouts?: Shout[]
topRatedMonthLayoutShouts?: Shout[]
}
export const useArticlesStore = (initialState: InitialState = {}) => {
addArticles([...(initialState.sortedArticles || [])])
if (initialState.sortedArticles) {
setSortedArticles([...initialState.sortedArticles])
}
export const useLayoutsStore = (layout: LayoutType, initialData: Shout[]) => {
addLayoutShouts(layout, initialData || [])
return {
articleEntities,
sortedArticles,
topArticles,
topMonthArticles,
topViewedArticles,
topCommentedArticles,
articlesByLayout
addLayoutShouts,
sortedLayoutShouts,
loadSearchResults,
loadRecentLayoutShouts,
loadTopMonthLayoutShouts,
loadTopLayoutShouts
}
}

View File

@ -29,7 +29,10 @@ import authorsBySlugs from '../graphql/query/authors-by-slugs'
import incrementView from '../graphql/mutation/increment-view'
import createArticle from '../graphql/mutation/article-create'
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
@ -147,11 +150,13 @@ export const apiClient = {
getSearchResults: async ({
query,
limit = FEED_SIZE,
offset = 0
offset = 0,
layout = 'literature'
}: {
query: string
limit: number
offset?: number
layout?: LayoutType
}): Promise<Shout[]> => {
const response = await publicGraphQLClient
.query(searchResults, {
@ -336,8 +341,18 @@ export const apiClient = {
const resp = await privateGraphQLClient.query(myChats, payload).toPromise()
return resp.data.myChats
},
getLayoutShouts: async ({ layout = 'article', amount = 50, offset = 0 }) => {
const resp = await publicGraphQLClient.query(getLayout, { amount, offset, layout }).toPromise()
return resp.data.shoutsByLayout
getRecentLayoutShouts: async ({ layout = 'article', amount = 50, offset = 0 }) => {
const resp = await publicGraphQLClient.query(getRecentByLayout, { amount, offset, layout }).toPromise()
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
}
}