catch up with backend refactoring

This commit is contained in:
Igor Lobanov 2022-11-18 03:23:04 +01:00
parent ee06ab54d2
commit 46b4ecc7ea
34 changed files with 213 additions and 140 deletions

View File

@ -1,11 +1,4 @@
# Astro + Solid.js
Created with
``` ```
npm init astro -- --template framework-solid yarn install
npm start
``` ```
Astro working with [Solid](https://www.solidjs.com/).
Write your Solid components as `.jsx` or `.tsx` files in your project.

View File

@ -1,7 +1,7 @@
import { defineConfig, AstroUserConfig } from 'astro/config' 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, PluginOption } from 'vite' import type { CSSOptions } from 'vite'
import defaultGenerateScopedName from 'postcss-modules/build/generateScopedName' 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'

View File

@ -1,5 +1,5 @@
overwrite: true overwrite: true
schema: 'https://testapi.discours.io/graphql' schema: 'http://v2.discours.io/graphql'
generates: generates:
src/graphql/introspec.gen.ts: src/graphql/introspec.gen.ts:
plugins: plugins:

View File

@ -1,6 +1,8 @@
.Message { .Message {
.own { .own {
// TODO
} }
.body { .body {
// message text // message text
} }

View File

@ -1,7 +1,7 @@
import { PageWrap } from '../_shared/PageWrap' import { PageWrap } from '../_shared/PageWrap'
import { ArticleView } from '../Views/Article' import { ArticleView } from '../Views/Article'
import type { PageProps } from '../types' import type { PageProps } from '../types'
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles' import { loadShout, useArticlesStore } from '../../stores/zine/articles'
import { createMemo, onMount, Show } from 'solid-js' import { createMemo, onMount, Show } from 'solid-js'
import type { Shout } from '../../graphql/types.gen' import type { Shout } from '../../graphql/types.gen'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
@ -32,7 +32,7 @@ export const ArticlePage = (props: PageProps) => {
const articleValue = articleEntities()[slug()] const articleValue = articleEntities()[slug()]
if (!articleValue || !articleValue.body) { if (!articleValue || !articleValue.body) {
await loadShoutsBy({ by: { slug: slug() }, limit: 1, offset: 0 }) await loadShout(slug())
} }
}) })

View File

@ -2,7 +2,7 @@ import { PageWrap } from '../_shared/PageWrap'
import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../Views/Author' import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../Views/Author'
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 { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles' import { loadShouts, resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
import { loadAuthor } from '../../stores/zine/authors' import { loadAuthor } from '../../stores/zine/authors'
import { Loading } from '../Loading' import { Loading } from '../Loading'
@ -27,7 +27,7 @@ export const AuthorPage = (props: PageProps) => {
return return
} }
await loadShoutsBy({ by: { author: slug() }, limit: PRERENDERED_ARTICLES_COUNT }) await loadShouts({ filters: { author: slug() }, limit: PRERENDERED_ARTICLES_COUNT })
await loadAuthor({ slug: slug() }) await loadAuthor({ slug: slug() })
setIsLoaded(true) setIsLoaded(true)

View File

@ -2,7 +2,7 @@ import { HomeView, PRERENDERED_ARTICLES_COUNT } from '../Views/Home'
import { PageWrap } from '../_shared/PageWrap' import { PageWrap } from '../_shared/PageWrap'
import type { PageProps } from '../types' import type { PageProps } from '../types'
import { createSignal, onCleanup, onMount, Show } from 'solid-js' import { createSignal, onCleanup, onMount, Show } from 'solid-js'
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles' import { loadShouts, resetSortedArticles } from '../../stores/zine/articles'
import { loadRandomTopics } from '../../stores/zine/topics' import { loadRandomTopics } from '../../stores/zine/topics'
import { Loading } from '../Loading' import { Loading } from '../Loading'
import styles from './HomePage.module.scss' import styles from './HomePage.module.scss'
@ -15,7 +15,7 @@ export const HomePage = (props: PageProps) => {
return return
} }
await loadShoutsBy({ by: { visibility: 'public' }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 }) await loadShouts({ filters: { visibility: 'public' }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
await loadRandomTopics() await loadRandomTopics()
setIsLoaded(true) setIsLoaded(true)

View File

@ -1,14 +1,14 @@
import { PageWrap } from '../_shared/PageWrap' import { PageWrap } from '../_shared/PageWrap'
import type { PageProps } from '../types' import type { PageProps } from '../types'
import { createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js' import { createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles' import { loadShouts, resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
import { LayoutType, useLayoutsStore } from '../../stores/zine/layouts' import { LayoutType, useLayoutsStore } from '../../stores/zine/layouts'
import { Loading } from '../Loading' import { Loading } from '../Loading'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll' import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import type { Shout } from '../../graphql/types.gen' import type { Shout } from '../../graphql/types.gen'
import { splitToPages } from '../../utils/splitToPages' import { splitToPages } from '../../utils/splitToPages'
import clsx from 'clsx' import { clsx } from 'clsx'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { Row3 } from '../Feed/Row3' import { Row3 } from '../Feed/Row3'
import { Row2 } from '../Feed/Row2' import { Row2 } from '../Feed/Row2'
@ -33,7 +33,8 @@ export const LayoutShoutsPage = (props: PageProps) => {
const loadMoreLayout = async (kind: LayoutType) => { const loadMoreLayout = async (kind: LayoutType) => {
saveScrollPosition() saveScrollPosition()
const { hasMore } = await loadLayoutShoutsBy({ const { hasMore } = await loadLayoutShoutsBy({
by: { layout: kind }, // filters: { layout: kind },
limit: LOAD_MORE_PAGE_SIZE, limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length offset: sortedArticles().length
}) })
@ -62,7 +63,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
onMount(async () => { onMount(async () => {
if (!isLoaded()) { if (!isLoaded()) {
await loadShoutsBy({ by: { layout: layout() }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 }) await loadShouts({ filters: { layout: layout() }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
} }
}) })

View File

@ -2,8 +2,8 @@ import { PageWrap } from '../_shared/PageWrap'
import { SearchView } from '../Views/Search' import { SearchView } from '../Views/Search'
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 { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
import { loadShouts, resetSortedArticles } from '../../stores/zine/articles'
import { Loading } from '../Loading' import { Loading } from '../Loading'
export const SearchPage = (props: PageProps) => { export const SearchPage = (props: PageProps) => {
@ -26,7 +26,7 @@ export const SearchPage = (props: PageProps) => {
return return
} }
await loadShoutsBy({ by: { title: q(), body: q() }, limit: 50, offset: 0 }) await loadShouts({ filters: { title: q(), body: q() }, limit: 50, offset: 0 })
setIsLoaded(true) setIsLoaded(true)
}) })

View File

@ -2,7 +2,7 @@ import { PageWrap } from '../_shared/PageWrap'
import { PRERENDERED_ARTICLES_COUNT, TopicView } from '../Views/Topic' import { PRERENDERED_ARTICLES_COUNT, TopicView } from '../Views/Topic'
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 { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles' import { loadShouts, resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
import { loadTopic } from '../../stores/zine/topics' import { loadTopic } from '../../stores/zine/topics'
import { Loading } from '../Loading' import { Loading } from '../Loading'
@ -27,7 +27,7 @@ export const TopicPage = (props: PageProps) => {
return return
} }
await loadShoutsBy({ by: { topics: [slug()] }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 }) await loadShouts({ filters: { topic: slug() }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
await loadTopic({ slug: slug() }) await loadTopic({ slug: slug() })
setIsLoaded(true) setIsLoaded(true)

View File

@ -5,7 +5,7 @@ import { Row3 } from '../Feed/Row3'
import { AuthorFull } from '../Author/Full' import { AuthorFull } from '../Author/Full'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { useAuthorsStore } from '../../stores/zine/authors' import { useAuthorsStore } from '../../stores/zine/authors'
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles' import { loadShouts, useArticlesStore } from '../../stores/zine/articles'
import { useTopicsStore } from '../../stores/zine/topics' import { useTopicsStore } from '../../stores/zine/topics'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
@ -42,8 +42,8 @@ export const AuthorView = (props: AuthorProps) => {
const loadMore = async () => { const loadMore = async () => {
saveScrollPosition() saveScrollPosition()
const { hasMore } = await loadShoutsBy({ const { hasMore } = await loadShouts({
by: { author: author().slug }, filters: { author: author().slug },
limit: LOAD_MORE_PAGE_SIZE, limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length offset: sortedArticles().length
}) })

View File

@ -8,7 +8,7 @@ import { AuthorCard } from '../Author/Card'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { FeedSidebar } from '../Feed/Sidebar' import { FeedSidebar } from '../Feed/Sidebar'
import CommentCard from '../Article/Comment' import CommentCard from '../Article/Comment'
import { useArticlesStore } from '../../stores/zine/articles' import { loadShouts, useArticlesStore } from '../../stores/zine/articles'
import { useReactionsStore } from '../../stores/zine/reactions' import { useReactionsStore } from '../../stores/zine/reactions'
import { useAuthorsStore } from '../../stores/zine/authors' import { useAuthorsStore } from '../../stores/zine/authors'
import { useTopicsStore } from '../../stores/zine/topics' import { useTopicsStore } from '../../stores/zine/topics'
@ -28,7 +28,7 @@ export const FEED_PAGE_SIZE = 20
export const FeedView = () => { export const FeedView = () => {
// state // state
const { sortedArticles, loadShoutsBy } = useArticlesStore() const { sortedArticles } = useArticlesStore()
const { sortedReactions: topComments, loadReactionsBy } = useReactionsStore({}) const { sortedReactions: topComments, loadReactionsBy } = useReactionsStore({})
const { sortedAuthors } = useAuthorsStore() const { sortedAuthors } = useAuthorsStore()
const { topTopics } = useTopicsStore() const { topTopics } = useTopicsStore()
@ -37,8 +37,8 @@ export const FeedView = () => {
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const loadMore = async () => { const loadMore = async () => {
const { hasMore } = await loadShoutsBy({ const { hasMore } = await loadShouts({
by: { visibility: 'community' }, filters: { visibility: 'community' },
limit: FEED_PAGE_SIZE, limit: FEED_PAGE_SIZE,
offset: sortedArticles().length offset: sortedArticles().length
}) })
@ -57,7 +57,7 @@ export const FeedView = () => {
// load recent editing shouts ( visibility = authors ) // load recent editing shouts ( visibility = authors )
const userslug = session().user.slug const userslug = session().user.slug
await loadShoutsBy({ by: { author: userslug, visibility: 'authors' }, limit: 15, offset: 0 }) await loadShouts({ filters: { author: userslug, visibility: 'authors' }, limit: 15, offset: 0 })
const collaborativeShouts = sortedArticles().filter((s: Shout, n: number, arr: Shout[]) => { const collaborativeShouts = sortedArticles().filter((s: Shout, n: number, arr: Shout[]) => {
if (s.visibility !== 'authors') { if (s.visibility !== 'authors') {
arr.splice(n, 1) arr.splice(n, 1)

View File

@ -14,7 +14,7 @@ import type { Shout, Topic } from '../../graphql/types.gen'
import { Icon } from '../_shared/Icon' import { Icon } from '../_shared/Icon'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { useTopicsStore } from '../../stores/zine/topics' import { useTopicsStore } from '../../stores/zine/topics'
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles' import { loadShouts, useArticlesStore } from '../../stores/zine/articles'
import { useTopAuthorsStore } from '../../stores/zine/topAuthors' import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
import { locale } from '../../stores/ui' import { locale } from '../../stores/ui'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll' import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
@ -48,8 +48,7 @@ export const HomeView = (props: HomeProps) => {
onMount(async () => { onMount(async () => {
if (sortedArticles().length < PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT) { if (sortedArticles().length < PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT) {
const { hasMore } = await loadShoutsBy({ const { hasMore } = await loadShouts({
by: {},
limit: CLIENT_LOAD_ARTICLES_COUNT, limit: CLIENT_LOAD_ARTICLES_COUNT,
offset: sortedArticles().length offset: sortedArticles().length
}) })
@ -77,8 +76,8 @@ export const HomeView = (props: HomeProps) => {
const loadMore = async () => { const loadMore = async () => {
saveScrollPosition() saveScrollPosition()
const { hasMore } = await loadShoutsBy({ const { hasMore } = await loadShouts({
by: { visibility: 'public' }, filters: { visibility: 'public' },
limit: LOAD_MORE_PAGE_SIZE, limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length offset: sortedArticles().length
}) })

View File

@ -3,7 +3,7 @@ import '../../styles/Search.scss'
import type { Shout } from '../../graphql/types.gen' import type { Shout } from '../../graphql/types.gen'
import { ArticleCard } from '../Feed/Card' import { ArticleCard } from '../Feed/Card'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { useArticlesStore, loadShoutsBy } from '../../stores/zine/articles' import { loadShouts, useArticlesStore } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
type SearchPageSearchParams = { type SearchPageSearchParams = {
@ -28,7 +28,7 @@ export const SearchView = (props: Props) => {
const handleSubmit = (_ev) => { const handleSubmit = (_ev) => {
// TODO page // TODO page
// TODO sort // TODO sort
loadShoutsBy({ by: { title: getQuery(), body: getQuery() }, limit: 50 }) loadShouts({ filters: { title: getQuery(), body: getQuery() }, limit: 50 })
} }
return ( return (

View File

@ -8,7 +8,7 @@ import { FullTopic } from '../Topic/Full'
import { t } from '../../utils/intl' import { t } from '../../utils/intl'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
import { useTopicsStore } from '../../stores/zine/topics' import { useTopicsStore } from '../../stores/zine/topics'
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles' import { loadShouts, useArticlesStore } from '../../stores/zine/articles'
import { useAuthorsStore } from '../../stores/zine/authors' import { useAuthorsStore } from '../../stores/zine/authors'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll' import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import { splitToPages } from '../../utils/splitToPages' import { splitToPages } from '../../utils/splitToPages'
@ -44,8 +44,8 @@ export const TopicView = (props: TopicProps) => {
const loadMore = async () => { const loadMore = async () => {
saveScrollPosition() saveScrollPosition()
const { hasMore } = await loadShoutsBy({ const { hasMore } = await loadShouts({
by: { topic: topic().slug }, filters: { topic: topic().slug },
limit: LOAD_MORE_PAGE_SIZE, limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length offset: sortedArticles().length
}) })

View File

@ -1,8 +1,8 @@
import { gql } from '@urql/core' import { gql } from '@urql/core'
export default gql` export default gql`
query LoadShoutsByQuery($by: ShoutsBy, $limit: Int!, $offset: Int!) { query LoadShoutsQuery($options: LoadShoutsOptions) {
loadShoutsBy(by: $by, limit: $limit, offset: $offset) { loadShouts(options: $options) {
_id: slug _id: slug
title title
subtitle subtitle

View File

@ -0,0 +1,42 @@
import { gql } from '@urql/core'
export default gql`
query LoadShoutQuery($slug: String!) {
loadShout(slug: $slug) {
_id: slug
title
subtitle
slug
layout
cover
body
# 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
rating
}
}
}
`

View File

@ -12,11 +12,11 @@ export default gql`
links links
createdAt createdAt
lastSeen lastSeen
ratings { # ratings {
_id: rater # _id: rater
rater # rater
value # value
} # }
} }
} }
` `

View File

@ -8,15 +8,15 @@ export default gql`
name name
bio bio
userpic userpic
communities # communities
links links
createdAt # createdAt
lastSeen lastSeen
ratings { # ratings {
_id: rater # _id: rater
rater # rater
value # value
} # }
} }
} }
` `

View File

@ -1,7 +1,7 @@
import { gql } from '@urql/core' import { gql } from '@urql/core'
export default gql` export default gql`
query LoadReactionsByQuery($by: ReactionsBy, $limit: Int!, $offset: Int!) { query LoadReactionsByQuery($by: ReactionBy!, $limit: Int!, $offset: Int!) {
loadReactionsBy(by: $by, limit: $limit, offset: $offset) { loadReactionsBy(by: $by, limit: $limit, offset: $offset) {
id id
createdBy { createdBy {

View File

@ -80,6 +80,14 @@ export type ChatMember = {
userpic?: Maybe<Scalars['String']> userpic?: Maybe<Scalars['String']>
} }
export type ChatUser = {
id: Scalars['Int']
lastSeen?: Maybe<Scalars['DateTime']>
name: Scalars['String']
slug: Scalars['String']
userpic?: Maybe<Scalars['String']>
}
export type Collab = { export type Collab = {
authors: Array<Maybe<Scalars['String']>> authors: Array<Maybe<Scalars['String']>>
body?: Maybe<Scalars['String']> body?: Maybe<Scalars['String']>
@ -116,6 +124,25 @@ export enum FollowingEntity {
Topic = 'TOPIC' Topic = 'TOPIC'
} }
export type LoadShoutsFilters = {
author?: InputMaybe<Scalars['String']>
body?: InputMaybe<Scalars['String']>
days?: InputMaybe<Scalars['Int']>
layout?: InputMaybe<Scalars['String']>
reacted?: InputMaybe<Scalars['Boolean']>
title?: InputMaybe<Scalars['String']>
topic?: InputMaybe<Scalars['String']>
visibility?: InputMaybe<Scalars['String']>
}
export type LoadShoutsOptions = {
filters?: InputMaybe<LoadShoutsFilters>
limit: Scalars['Int']
offset?: InputMaybe<Scalars['Int']>
order_by?: InputMaybe<Scalars['String']>
order_by_desc?: InputMaybe<Scalars['Boolean']>
}
export type Message = { export type Message = {
author: Scalars['String'] author: Scalars['String']
body: Scalars['String'] body: Scalars['String']
@ -183,7 +210,7 @@ export type MutationCreateChatArgs = {
export type MutationCreateMessageArgs = { export type MutationCreateMessageArgs = {
body: Scalars['String'] body: Scalars['String']
chatId: Scalars['String'] chat: Scalars['String']
replyTo?: InputMaybe<Scalars['String']> replyTo?: InputMaybe<Scalars['String']>
} }
@ -317,15 +344,17 @@ export type ProfileInput = {
export type Query = { export type Query = {
authorsAll: Array<Maybe<Author>> authorsAll: Array<Maybe<Author>>
getAuthor: User chatUsersAll: Array<Maybe<ChatUser>>
getAuthor?: Maybe<User>
getCollabs: Array<Maybe<Collab>> getCollabs: Array<Maybe<Collab>>
getTopic: Topic getTopic?: Maybe<Topic>
isEmailUsed: Scalars['Boolean'] isEmailUsed: Scalars['Boolean']
loadAuthorsBy: Array<Maybe<Author>> loadAuthorsBy: Array<Maybe<Author>>
loadChats: Result loadChats: Result
loadMessagesBy: Result loadMessagesBy: Result
loadReactionsBy: Array<Maybe<Reaction>> loadReactionsBy: Array<Maybe<Reaction>>
loadShoutsBy: Array<Maybe<Shout>> loadShout?: Maybe<Shout>
loadShouts: Array<Maybe<Shout>>
markdownBody: Scalars['String'] markdownBody: Scalars['String']
searchUsers: Result searchUsers: Result
signIn: AuthResult signIn: AuthResult
@ -374,10 +403,12 @@ export type QueryLoadReactionsByArgs = {
offset?: InputMaybe<Scalars['Int']> offset?: InputMaybe<Scalars['Int']>
} }
export type QueryLoadShoutsByArgs = { export type QueryLoadShoutArgs = {
by?: InputMaybe<ShoutsBy> slug: Scalars['String']
limit?: InputMaybe<Scalars['Int']> }
offset?: InputMaybe<Scalars['Int']>
export type QueryLoadShoutsArgs = {
options?: InputMaybe<LoadShoutsOptions>
} }
export type QueryMarkdownBodyArgs = { export type QueryMarkdownBodyArgs = {
@ -563,12 +594,12 @@ export type ShoutInput = {
visibleForUsers?: InputMaybe<Array<InputMaybe<Scalars['String']>>> visibleForUsers?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
} }
export type ShoutsBy = { export type ShoutsFilterBy = {
author?: InputMaybe<Scalars['String']> author?: InputMaybe<Scalars['String']>
authors?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
body?: InputMaybe<Scalars['String']> body?: InputMaybe<Scalars['String']>
days?: InputMaybe<Scalars['Int']> days?: InputMaybe<Scalars['Int']>
layout?: InputMaybe<Scalars['String']> layout?: InputMaybe<Scalars['String']>
order?: InputMaybe<Scalars['String']>
slug?: InputMaybe<Scalars['String']> slug?: InputMaybe<Scalars['String']>
stat?: InputMaybe<Scalars['String']> stat?: InputMaybe<Scalars['String']>
title?: InputMaybe<Scalars['String']> title?: InputMaybe<Scalars['String']>

View File

@ -9,7 +9,7 @@ if (slug.endsWith('.map')) {
return Astro.redirect('/404') return Astro.redirect('/404')
} }
const article = await apiClient.loadShoutsBy({ by: { slug }, limit: 1}) const article = await apiClient.getShout(slug)
if (!article) { if (!article) {
return Astro.redirect('/404') return Astro.redirect('/404')
} }
@ -21,5 +21,5 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate'
--- ---
<Prerendered> <Prerendered>
<Root article={article.at(0)} client:load /> <Root article={article} client:load />
</Prerendered> </Prerendered>

View File

@ -6,8 +6,8 @@ import { initRouter } from '../../../stores/router'
import { PRERENDERED_ARTICLES_COUNT } from '../../../components/Views/Author' import { PRERENDERED_ARTICLES_COUNT } from '../../../components/Views/Author'
const slug = Astro.params.slug.toString() const slug = Astro.params.slug.toString()
const shouts = await apiClient.loadShoutsBy({ by: { author: slug } , limit: PRERENDERED_ARTICLES_COUNT }) const shouts = await apiClient.getShouts({ filters: { author: slug }, limit: PRERENDERED_ARTICLES_COUNT })
const author = await apiClient.loadAuthorsBy({ by: { slug } }) const author = await apiClient.getAuthorsBy({ by: { slug } })
const { pathname, search } = Astro.url const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)

View File

@ -4,13 +4,12 @@ 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 type { LayoutType } from '../../stores/zine/layouts' import type { LayoutType } from '../../stores/zine/layouts'
import { Layout } from '../../components/EditorExample/components/Layout'
const layout = (Astro.params.layout?.toString() || 'article') as LayoutType const layout = (Astro.params.layout?.toString() || 'article') as LayoutType
if (!layout || layout.endsWith('.map')) { if (!layout || layout.endsWith('.map')) {
return Astro.redirect('/404') return Astro.redirect('/404')
} }
const shouts = await apiClient.loadShoutsBy({ by: { layout } }) const shouts = await apiClient.getShouts({ filters: { layout }, limit: 50 })
const { pathname, search } = Astro.url const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)
--- ---

View File

@ -1,7 +1,6 @@
--- ---
import Prerendered from '../main.astro' import Prerendered from '../main.astro'
import { Root } from '../components/Root' import { Root } from '../components/Root'
import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router' import { initRouter } from '../stores/router'
const { pathname, search } = Astro.url const { pathname, search } = Astro.url

View File

@ -6,8 +6,8 @@ import { initRouter } from '../stores/router'
import { PRERENDERED_ARTICLES_COUNT } from '../components/Views/Home' import { PRERENDERED_ARTICLES_COUNT } from '../components/Views/Home'
const randomTopics = await apiClient.getRandomTopics({ amount: 12 }) const randomTopics = await apiClient.getRandomTopics({ amount: 12 })
const articles = await apiClient.loadShoutsBy( const articles = await apiClient.getShouts(
{ by: { visibility: "public" }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 }) { filters: { visibility: "public" }, limit: PRERENDERED_ARTICLES_COUNT })
const { pathname, search } = Astro.url const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)

View File

@ -6,7 +6,7 @@ import { initRouter } from '../stores/router'
const params: URLSearchParams = Astro.url.searchParams const params: URLSearchParams = Astro.url.searchParams
const q = params.get('q') const q = params.get('q')
const searchResults = await apiClient.loadShoutsBy({ by: { title: q, body: q }, limit: 50 }) const searchResults = await apiClient.getShouts({ filters: { title: q, body: q }, limit: 50 })
const { pathname, search } = Astro.url const { pathname, search } = Astro.url
initRouter(pathname, search) initRouter(pathname, search)

View File

@ -5,7 +5,7 @@ import { apiClient } from '../../utils/apiClient'
import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Topic' import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Topic'
const slug = Astro.params.slug?.toString() || '' const slug = Astro.params.slug?.toString() || ''
const shouts = await apiClient.loadShoutsBy({ by: { topics: [slug] }, limit: PRERENDERED_ARTICLES_COUNT }) const shouts = await apiClient.getShouts({ filters: { topic: slug }, limit: PRERENDERED_ARTICLES_COUNT })
const topic = await apiClient.getTopic({ slug }) const topic = await apiClient.getTopic({ slug })
import { initRouter } from '../../stores/router' import { initRouter } from '../../stores/router'

View File

@ -1,4 +1,4 @@
import type { Author, Shout, ShoutInput, ShoutsBy, Topic } from '../../graphql/types.gen' import type { Author, Shout, ShoutInput, Topic, LoadShoutsOptions } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient' import { apiClient } from '../../utils/apiClient'
import { addAuthorsByTopic } from './authors' import { addAuthorsByTopic } from './authors'
import { addTopicsByAuthor } from './topics' import { addTopicsByAuthor } from './topics'
@ -123,22 +123,18 @@ const addSortedArticles = (articles: Shout[]) => {
setSortedArticles((prevSortedArticles) => [...prevSortedArticles, ...articles]) setSortedArticles((prevSortedArticles) => [...prevSortedArticles, ...articles])
} }
export const loadShoutsBy = async ({ export const loadShout = async (slug: string): Promise<void> => {
by, const newArticle = await apiClient.getShout(slug)
limit, addArticles([newArticle])
offset = 0 }
}: {
by: ShoutsBy export const loadShouts = async (options: LoadShoutsOptions): Promise<{ hasMore: boolean }> => {
limit: number const newArticles = await apiClient.getShouts({
offset?: number ...options,
}): Promise<{ hasMore: boolean }> => { limit: options.limit + 1
const newArticles = await apiClient.loadShoutsBy({
by,
limit: limit + 1,
offset
}) })
const hasMore = newArticles.length === limit + 1 const hasMore = newArticles.length === options.limit + 1
if (hasMore) { if (hasMore) {
newArticles.splice(-1) newArticles.splice(-1)
@ -176,7 +172,6 @@ export const useArticlesStore = (initialState: InitialState = {}) => {
return { return {
articleEntities, articleEntities,
sortedArticles, sortedArticles,
loadShoutsBy,
articlesByAuthor, articlesByAuthor,
articlesByLayout, articlesByLayout,
articlesByTopic, articlesByTopic,

View File

@ -38,7 +38,7 @@ const sortedAuthors = createLazyMemo(() => {
}) })
const addAuthors = (authors: Author[]) => { const addAuthors = (authors: Author[]) => {
const newAuthorEntities = authors.reduce((acc, author) => { const newAuthorEntities = authors.filter(Boolean).reduce((acc, author) => {
acc[author.slug] = author acc[author.slug] = author
return acc return acc
}, {} as Record<string, Author>) }, {} as Record<string, Author>)

View File

@ -1,6 +1,5 @@
import type { Shout, ShoutsBy } from '../../graphql/types.gen' import type { Shout, LoadShoutsOptions } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient' import { apiClient } from '../../utils/apiClient'
import { useArticlesStore } from './articles'
import { createSignal } from 'solid-js' import { createSignal } from 'solid-js'
export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature' export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature'
@ -22,27 +21,18 @@ export const resetSortedLayoutShouts = () => {
setSortedLayoutShouts(new Map()) setSortedLayoutShouts(new Map())
} }
export const loadLayoutShoutsBy = async ({ export const loadLayoutShoutsBy = async (options: LoadShoutsOptions): Promise<{ hasMore: boolean }> => {
by, const newLayoutShouts = await apiClient.getShouts({
limit, ...options,
offset limit: options.limit + 1
}: {
by: ShoutsBy
limit?: number
offset?: number
}): Promise<{ hasMore: boolean }> => {
const newLayoutShouts = await apiClient.loadShoutsBy({
by,
limit: limit + 1,
offset
}) })
const hasMore = newLayoutShouts.length === limit + 1 const hasMore = newLayoutShouts.length === options.limit + 1
if (hasMore) { if (hasMore) {
newLayoutShouts.splice(-1) newLayoutShouts.splice(-1)
} }
addLayoutShouts(by.layout as LayoutType, newLayoutShouts) addLayoutShouts(options.filters.layout as LayoutType, newLayoutShouts)
return { hasMore } return { hasMore }
} }

View File

@ -16,7 +16,7 @@ export const loadReactionsBy = async ({
limit?: number limit?: number
offset?: number offset?: number
}): Promise<{ hasMore: boolean }> => { }): Promise<{ hasMore: boolean }> => {
const data = await apiClient.loadReactionsBy({ by, limit: limit + 1, offset }) const data = await apiClient.getReactionsBy({ by, limit: limit + 1, offset })
const hasMore = data.length === limit + 1 const hasMore = data.length === limit + 1
if (hasMore) data.splice(-1) if (hasMore) data.splice(-1)
// TODO: const [data, provider] = roomConnect(articleSlug, username, "reactions") // TODO: const [data, provider] = roomConnect(articleSlug, username, "reactions")

View File

@ -7,18 +7,19 @@ main {
.messages { .messages {
display: none; display: none;
//top: 74px;
//height: calc(100% - 74px); // top: 74px;
//left: 0; // height: calc(100% - 74px);
//right: 0; // left: 0;
//padding-left: 42px; // right: 0;
//padding-right: 26px; // padding-left: 42px;
//background: #fff; // padding-right: 26px;
//display: flex; // background: #fff;
//flex: 1; // display: flex;
//flex-direction: column; // flex: 1;
//position: fixed; // flex-direction: column;
//z-index: 9; // position: fixed;
// z-index: 9;
> .row { > .row {
flex: 1; flex: 1;
@ -165,10 +166,10 @@ main {
.message-form { .message-form {
background: #fff; background: #fff;
padding: 2px 0 12px 0; padding: 2px 0 12px;
> .wrapper { > .wrapper {
border: 2px solid #cccccc; border: 2px solid #ccc;
border-radius: 16px; border-radius: 16px;
padding: 4px; padding: 4px;
display: flex; display: flex;
@ -291,6 +292,7 @@ main {
a { a {
color: inherit; color: inherit;
text-decoration: underline; text-decoration: underline;
&:hover { &:hover {
color: inherit; color: inherit;
} }

View File

@ -1,4 +1,11 @@
import type { FollowingEntity, AuthResult, ShoutInput, Topic, Author } from '../graphql/types.gen' import type {
FollowingEntity,
AuthResult,
ShoutInput,
Topic,
Author,
LoadShoutsOptions
} from '../graphql/types.gen'
import { publicGraphQLClient } from '../graphql/publicGraphQLClient' import { publicGraphQLClient } from '../graphql/publicGraphQLClient'
import { getToken, privateGraphQLClient } from '../graphql/privateGraphQLClient' import { getToken, privateGraphQLClient } from '../graphql/privateGraphQLClient'
import topicsAll from '../graphql/query/topics-all' import topicsAll from '../graphql/query/topics-all'
@ -26,8 +33,7 @@ import reactionsLoadBy from '../graphql/query/reactions-load-by'
import { REACTIONS_AMOUNT_PER_PAGE } from '../stores/zine/reactions' import { REACTIONS_AMOUNT_PER_PAGE } from '../stores/zine/reactions'
import authorsLoadBy from '../graphql/query/authors-load-by' import authorsLoadBy from '../graphql/query/authors-load-by'
import shoutsLoadBy from '../graphql/query/articles-load-by' import shoutsLoadBy from '../graphql/query/articles-load-by'
import shoutLoad from '../graphql/query/articles-load'
const FEED_SIZE = 50
type ApiErrorCode = type ApiErrorCode =
| 'unknown' | 'unknown'
@ -194,6 +200,7 @@ export const apiClient = {
}, },
getAuthor: async ({ slug }: { slug: string }): Promise<Author> => { getAuthor: async ({ slug }: { slug: string }): Promise<Author> => {
const response = await publicGraphQLClient.query(authorBySlug, { slug }).toPromise() const response = await publicGraphQLClient.query(authorBySlug, { slug }).toPromise()
console.error('getAuthor', response)
return response.data.getAuthor return response.data.getAuthor
}, },
getTopic: async ({ slug }: { slug: string }): Promise<Topic> => { getTopic: async ({ slug }: { slug: string }): Promise<Topic> => {
@ -229,20 +236,33 @@ export const apiClient = {
return response.data.deleteReaction return response.data.deleteReaction
}, },
// LOAD BY getAuthorsBy: async ({ by, limit = 50, offset = 0 }) => {
loadAuthorsBy: async ({ by, limit = 50, offset = 0 }) => {
const resp = await publicGraphQLClient.query(authorsLoadBy, { by, limit, offset }).toPromise() const resp = await publicGraphQLClient.query(authorsLoadBy, { by, limit, offset }).toPromise()
console.debug(resp) console.debug(resp)
return resp.data.loadShoutsBy return resp.data.loadAuthorsBy
}, },
loadShoutsBy: async ({ by, limit = 50, offset = 0 }) => { getShout: async (slug: string) => {
const resp = await publicGraphQLClient.query(shoutsLoadBy, { by, limit, offset }).toPromise() const resp = await publicGraphQLClient
console.debug(resp) .query(shoutLoad, {
return resp.data.loadShoutsBy slug
})
.toPromise()
return resp.data.loadShout
}, },
loadReactionsBy: async ({ by, limit = REACTIONS_AMOUNT_PER_PAGE, offset = 0 }) => { getShouts: async (options: LoadShoutsOptions) => {
const resp = await publicGraphQLClient
.query(shoutsLoadBy, {
options
})
.toPromise()
// console.debug(resp)
return resp.data.loadShouts
},
getReactionsBy: async ({ by, limit = REACTIONS_AMOUNT_PER_PAGE, offset = 0 }) => {
const resp = await publicGraphQLClient.query(reactionsLoadBy, { by, limit, offset }).toPromise() const resp = await publicGraphQLClient.query(reactionsLoadBy, { by, limit, offset }).toPromise()
console.log('resactions response', resp)
return resp.data.loadReactionsBy return resp.data.loadReactionsBy
}, },