wip-refactoring

This commit is contained in:
tonyrewin 2022-11-16 09:33:58 +03:00
parent f0f944e31a
commit 0c0084365d
10 changed files with 134 additions and 163 deletions

View File

@ -1,9 +1,9 @@
import { PageWrap } from '../_shared/PageWrap'
import type { PageProps } from '../types'
import { createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
import { resetSortedArticles } from '../../stores/zine/articles'
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles'
import { useRouter } from '../../stores/router'
import { LayoutType, loadRecentLayoutShouts, useLayoutsStore } from '../../stores/zine/layouts'
import { LayoutType, useLayoutsStore } from '../../stores/zine/layouts'
import { Loading } from '../Loading'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import type { Shout } from '../../graphql/types.gen'
@ -28,14 +28,13 @@ export const LayoutShoutsPage = (props: PageProps) => {
return page.params.layout as LayoutType
})
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const { sortedLayoutShouts } = useLayoutsStore(layout(), props.shouts)
const { sortedLayoutShouts, loadLayoutShoutsBy } = useLayoutsStore(layout(), props.shouts)
const sortedArticles = createMemo<Shout[]>(() => sortedLayoutShouts().get(layout()) || [])
const loadMoreLayout = async (kind: LayoutType) => {
saveScrollPosition()
const { hasMore } = await loadRecentLayoutShouts({
layout: kind,
amount: LOAD_MORE_PAGE_SIZE,
const { hasMore } = await loadLayoutShoutsBy({
by: { layout: kind },
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})
setIsLoadMoreButtonVisible(hasMore)
@ -63,7 +62,7 @@ export const LayoutShoutsPage = (props: PageProps) => {
onMount(async () => {
if (!isLoaded()) {
await loadRecentLayoutShouts({ layout: layout(), amount: PRERENDERED_ARTICLES_COUNT, offset: 0 })
await loadShoutsBy({ by: { layout: layout() }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
}
})

View File

@ -1,13 +1,14 @@
import { createEffect, createSignal, Show, Suspense } from 'solid-js'
import { FullArticle } from '../Article/FullArticle'
import { t } from '../../utils/intl'
import type { Shout } from '../../graphql/types.gen'
import { loadArticleReactions, useReactionsStore } from '../../stores/zine/reactions'
import type { Shout, Reaction } from '../../graphql/types.gen'
import { useReactionsStore } from '../../stores/zine/reactions'
import '../../styles/Article.scss'
interface ArticlePageProps {
article: Shout
reactions?: Reaction[]
}
const ARTICLE_COMMENTS_PAGE_SIZE = 50
@ -15,13 +16,20 @@ const ARTICLE_COMMENTS_PAGE_SIZE = 50
export const ArticleView = (props: ArticlePageProps) => {
const [getCommentsPage] = createSignal(1)
const [getIsCommentsLoading, setIsCommentsLoading] = createSignal(false)
const reactionslist = useReactionsStore()
const {
reactionsByShout,
sortedReactions,
createReaction,
updateReaction,
deleteReaction,
loadReactionsBy
} = useReactionsStore({ reactions: props.reactions })
createEffect(async () => {
try {
setIsCommentsLoading(true)
await loadArticleReactions({
articleSlug: props.article.slug,
await loadReactionsBy({
by: { shout: props.article.slug },
limit: ARTICLE_COMMENTS_PAGE_SIZE,
offset: getCommentsPage() * ARTICLE_COMMENTS_PAGE_SIZE
})
@ -36,7 +44,7 @@ export const ArticleView = (props: ArticlePageProps) => {
<Suspense>
<FullArticle
article={props.article}
reactions={reactionslist().filter((r) => r.shout.slug === props.article.slug)}
reactions={reactionsByShout()[props.article.slug]}
isCommentsLoading={getIsCommentsLoading()}
/>
</Suspense>

View File

@ -1,56 +1,41 @@
import { createMemo, createSignal, For, onMount, Show } from 'solid-js'
import { createEffect, createSignal, For, onMount, Show } from 'solid-js'
import '../../styles/Feed.scss'
import stylesBeside from '../../components/Feed/Beside.module.scss'
import { Icon } from '../_shared/Icon'
import { byCreated, sortBy } from '../../utils/sortby'
import { TopicCard } from '../Topic/Card'
import { ArticleCard } from '../Feed/Card'
import { AuthorCard } from '../Author/Card'
import { t } from '../../utils/intl'
import { FeedSidebar } from '../Feed/Sidebar'
import CommentCard from '../Article/Comment'
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles'
import { useArticlesStore } from '../../stores/zine/articles'
import { useReactionsStore } from '../../stores/zine/reactions'
import { useAuthorsStore } from '../../stores/zine/authors'
import { useTopicsStore } from '../../stores/zine/topics'
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
import { useSession } from '../../context/session'
import { Collab, ReactionKind, Shout } from '../../graphql/types.gen'
import { collabs, setCollabs } from '../../stores/editor'
// const AUTHORSHIP_REACTIONS = [
// ReactionKind.Accept,
// ReactionKind.Reject,
// ReactionKind.Propose,
// ReactionKind.Ask
// ]
const AUTHORSHIP_REACTIONS = [
ReactionKind.Accept,
ReactionKind.Reject,
ReactionKind.Propose,
ReactionKind.Ask
]
export const FEED_PAGE_SIZE = 20
export const FeedView = () => {
// state
const { sortedArticles } = useArticlesStore()
const reactions = useReactionsStore()
const { sortedArticles, loadShoutsBy } = useArticlesStore()
const { sortedReactions: topComments, loadReactionsBy } = useReactionsStore({})
const { sortedAuthors } = useAuthorsStore()
const { topTopics } = useTopicsStore()
const { topAuthors } = useTopAuthorsStore()
const { session } = useSession()
const topReactions = createMemo(() => sortBy(reactions(), byCreated))
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
// const expectingFocus = createMemo<Shout[]>(() => {
// // 1 co-author notifications needs
// // TODO: list of articles where you are co-author
// // TODO: preload proposals
// // TODO: (maybe?) and changes history
// console.debug(reactions().filter((r) => r.kind in AUTHORSHIP_REACTIONS))
//
// // 2 community self-regulating mechanics
// // TODO: query all new posts to be rated for publishing
// // TODO: query all reactions where user is in authors list
// return []
// })
const loadMore = async () => {
const { hasMore } = await loadShoutsBy({
by: { visibility: 'community' },
@ -60,8 +45,27 @@ export const FeedView = () => {
setIsLoadMoreButtonVisible(hasMore)
}
onMount(() => {
loadMore()
onMount(async () => {
// load 5 recent comments overall
await loadReactionsBy({ by: {}, limit: 5, offset: 0 })
// load recent shouts not only published ( visibility = community )
await loadMore()
// TODO: load collabs
// await loadCollabs()
// load recent editing shouts ( visibility = authors )
const userslug = session().user.slug
await loadShoutsBy({ by: { author: userslug, visibility: 'authors' }, limit: 15, offset: 0 })
const collaborativeShouts = sortedArticles().filter((s: Shout, n: number, arr: Shout[]) => {
if (s.visibility !== 'authors') {
arr.splice(n, 1)
return arr
}
})
// load recent reactions on collabs
await loadReactionsBy({ by: { shouts: [...collaborativeShouts], body: true }, limit: 5, offset: 0 })
})
return (
@ -122,7 +126,7 @@ export const FeedView = () => {
<aside class="col-md-3">
<section class="feed-comments">
<h4>{t('Comments')}</h4>
<For each={topReactions()}>
<For each={topComments()}>
{(comment) => <CommentCard comment={comment} compact={true} />}
</For>
</section>

View File

@ -9,7 +9,7 @@ if (slug.endsWith('.map')) {
return Astro.redirect('/404')
}
const article = await apiClient.loadShoutsBy({ by: { slug }, amount: 1})
const article = await apiClient.loadShoutsBy({ by: { slug }, limit: 1})
if (!article) {
return Astro.redirect('/404')
}

View File

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

View File

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

View File

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

View File

@ -1,8 +1,7 @@
import type { Shout } from '../../graphql/types.gen'
import type { Shout, ShoutsBy } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient'
import { useArticlesStore } from './articles'
import { createSignal } from 'solid-js'
import { byCreated } from '../../utils/sortby'
export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature'
@ -23,66 +22,29 @@ export const resetSortedLayoutShouts = () => {
setSortedLayoutShouts(new Map())
}
export const loadRecentLayoutShouts = async ({
layout,
amount,
offset
}: {
layout: LayoutType
amount: number
offset?: number
}): Promise<{ hasMore: boolean }> => {
const layoutShouts: Shout[] = await apiClient.loadShoutsBy({ by: { layout }, amount, offset })
const hasMore = layoutShouts.length < amount
if (hasMore) layoutShouts.splice(-1)
const shouts = layoutShouts.sort(byCreated)
const { articlesByLayout } = useArticlesStore({ shouts })
addLayoutShouts(layout, articlesByLayout()[layout])
return { hasMore }
}
export const loadTopMonthLayoutShouts = async (
layout: LayoutType,
amount: number,
offset: number
): Promise<{ hasMore: boolean }> => {
const shouts = await apiClient.loadShoutsBy({ by: { layout, stat: 'rating', days: 30 } })
const hasMore = shouts.length < amount
if (hasMore) shouts.splice(-1)
addLayoutShouts(layout, shouts)
return { hasMore }
}
export const loadTopLayoutShouts = async (
layout: LayoutType,
amount,
offset
): Promise<{ hasMore: boolean }> => {
const shouts = await apiClient.loadShoutsBy({ by: { layout, stat: 'rating' } })
const hasMore = shouts.length < amount
if (hasMore) shouts.splice(-1)
addLayoutShouts(layout, shouts)
return { hasMore }
}
export const loadShoutsSearch = async ({
layout,
query,
export const loadLayoutShoutsBy = async ({
by,
limit,
offset
}: {
layout: LayoutType
query: string
by: ShoutsBy
limit?: number
offset?: number
}): Promise<void> => {
const by = {
layout: layout,
query: query
}): Promise<{ hasMore: boolean }> => {
const newLayoutShouts = await apiClient.loadShoutsBy({
by,
limit: limit + 1,
offset
})
const hasMore = newLayoutShouts.length === limit + 1
if (hasMore) {
newLayoutShouts.splice(-1)
}
const amount = limit
const newLayoutShouts = await apiClient.loadShoutsBy({ by, amount, offset })
addLayoutShouts(layout, newLayoutShouts)
addLayoutShouts(by.layout as LayoutType, newLayoutShouts)
return { hasMore }
}
export const useLayoutsStore = (layout: LayoutType, initialData: Shout[]) => {
@ -91,6 +53,6 @@ export const useLayoutsStore = (layout: LayoutType, initialData: Shout[]) => {
return {
addLayoutShouts,
sortedLayoutShouts,
loadShoutsSearch
loadLayoutShoutsBy
}
}

View File

@ -1,63 +1,52 @@
import { atom, WritableAtom } from 'nanostores'
import type { Reaction } from '../../graphql/types.gen'
import { useStore } from '@nanostores/solid'
import type { Reaction, Shout } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient'
import { reduceBy } from '../../utils/reduce'
// import { roomConnect } from '../../utils/p2p'
// FIXME
import { createSignal } from 'solid-js'
// TODO: import { roomConnect } from '../../utils/p2p'
export const REACTIONS_AMOUNT_PER_PAGE = 100
const [sortedReactions, setSortedReactions] = createSignal<Reaction[]>([])
const [reactionsByShout, setReactionsByShout] = createSignal<{ [articleSlug: string]: Reaction[] }>({})
let reactionsOrdered: WritableAtom<Reaction[]>
export const reactions = atom<{ [slug: string]: Reaction[] }>({}) // by shout
export const useReactionsStore = (initial?: Reaction[]) => {
if (!reactionsOrdered) {
reactionsOrdered = atom(initial || [])
reactionsOrdered.listen((rrr: Reaction[]) => reactions.set(reduceBy(rrr, 'shout')))
}
return useStore(reactionsOrdered)
}
export const loadArticleReactions = async ({
articleSlug,
export const loadReactionsBy = async ({
by,
limit = REACTIONS_AMOUNT_PER_PAGE,
offset = 0
}: {
articleSlug: string
by
limit?: number
offset?: number
}): Promise<void> => {
const data = await apiClient.loadReactionsBy({ by: { shout: articleSlug }, amount: limit, offset })
}): Promise<{ hasMore: boolean }> => {
const data = await apiClient.loadReactionsBy({ by, limit: limit + 1, offset })
const hasMore = data.length === limit + 1
if (hasMore) data.splice(-1)
// TODO: const [data, provider] = roomConnect(articleSlug, username, "reactions")
reactionsOrdered.set(data)
setSortedReactions(data)
return { hasMore }
}
export const createReaction = async (reaction: Reaction) => {
const { reaction: r } = await apiClient.createReaction({ reaction })
return r
}
export const updateReaction = async (reaction: Reaction) => {
const { reaction: r } = await apiClient.updateReaction({ reaction })
return r
}
export const loadReactions = async ({
shoutSlugs,
limit = 100,
offset = 0
}: {
shoutSlugs: string[]
limit: number
offset: number
}): Promise<void> => {
const reactionsForShouts = await apiClient.loadReactionsBy({
by: { shouts: shoutSlugs },
amount: limit,
offset
})
reactionsOrdered.set(reactionsForShouts)
export const deleteReaction = async (reactionId: number) => {
const resp = await apiClient.destroyReaction({ id: reactionId })
return resp
}
export const useReactionsStore = (initialState: { reactions?: Reaction[] }) => {
if (initialState.reactions) {
setSortedReactions([...initialState.reactions])
}
export const createReaction = async (reaction: Reaction) =>
// FIXME
reactionsOrdered.set(await apiClient.createReaction({ reaction }))
export const updateReaction = async (reaction: Reaction) =>
// FIXME
reactionsOrdered.set(await apiClient.updateReaction({ reaction }))
export const deleteReaction = async (reactionId: number) =>
// FIXME
reactionsOrdered.set(await apiClient.destroyReaction({ id: reactionId }))
return {
reactionsByShout,
sortedReactions,
createReaction,
updateReaction,
deleteReaction,
loadReactionsBy
}
}

View File

@ -210,6 +210,14 @@ export const apiClient = {
console.debug('[api-client] [api] create reaction mutation called')
return response.data.createReaction
},
// CUDL
createChat: async ({ title, members }) => {
return await privateGraphQLClient
.mutation(createChatQuery, { title: title, members: members })
.toPromise()
},
updateReaction: async ({ reaction }) => {
const response = await privateGraphQLClient.mutation(reactionUpdate, { reaction }).toPromise()
@ -220,16 +228,9 @@ export const apiClient = {
return response.data.deleteReaction
},
createChat: async ({ title, members }) => {
return await privateGraphQLClient
.mutation(createChatQuery, { title: title, members: members })
.toPromise()
},
getChats: async (payload = {}) => {
const resp = await privateGraphQLClient.query(myChats, payload).toPromise()
return resp.data.myChats
},
// LOAD BY
loadAuthorsBy: async ({ by, limit = 50, offset = 0 }) => {
const resp = await publicGraphQLClient.query(authorsLoadBy, { by, limit, offset }).toPromise()
console.debug(resp)
@ -244,6 +245,14 @@ export const apiClient = {
const resp = await publicGraphQLClient.query(reactionsLoadBy, { by, limit, offset }).toPromise()
return resp.data.loadReactionsBy
},
// inbox
getChats: async (payload = {}) => {
const resp = await privateGraphQLClient.query(myChats, payload).toPromise()
return resp.data.myChats
},
getChatMessages: async ({
chat,
amount = 50,