refactoring-wpi
This commit is contained in:
parent
e6a14c6fa1
commit
28f52ba745
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
4
public/icons/audio.svg
Normal 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 |
1
public/icons/literature.svg
Normal file
1
public/icons/literature.svg
Normal 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 |
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
40
src/graphql/query/layout-recent.ts
Normal file
40
src/graphql/query/layout-recent.ts
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
40
src/graphql/query/layout-top-month.ts
Normal file
40
src/graphql/query/layout-top-month.ts
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
|
@ -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
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
import { setLocale } from './stores/ui';
|
||||
import { setLocale } from './stores/ui'
|
||||
import './styles/app.scss'
|
||||
import { t } from './utils/intl'
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
12
src/pages/feed/settings.astro
Normal file
12
src/pages/feed/settings.astro
Normal 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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user